您的位置:首页 > 移动开发 > IOS开发

IOS 5编程 内存管理 ARC技术概述

2011-10-20 09:25 330 查看


ARC(Automatic
ReferenceCounting)技术概述

此文章由Tom翻译,首发于csdn的blog,任何人都可以转发,但是请保留原始链接和翻译者得名字。多谢!

AutomaticReferenceCounting(ARC)是一个编译期的技术,利用此技术可以简化Objective-C编程在内存管理方面的工作量。

这里我把此技术翻译为自动内存计数器管理技术,下图是使用和不使用此技术的Objective-C代码的区别。



ARC技术是随着XCode4.2一起发布的,在缺省工程模板中,你可以指定你的工程是否支持ARC技术,如果你不指定工程支持ARC技术,在代码中你必须使用管理内存的代码来管理内存。


概述

自动计数(ARC)是一个编译期间工作的能够帮你管理内存的技术,通过它,程序人员可以不需要在内存的retain,释放等方面花费精力。

ARC在编译期间为每个Objective-C指针变量添加合适的retain,release,autorelease等函数,保存每个变量的生存周期控制在合理的范围内,以期实现代码上的自动内存管理。

Inorderforthecompilertogeneratecorrectcode,ARCimposessomerestrictionsonthemethodsyoucanuse,andonhowyouusetoll-freebridging(see“Toll-Free
BridgedTypes”);ARCalsointroducesnewlifetimequalifiersforobjectreferencesanddeclared
properties.

你可以使用编译标记
-fobjc-arc来让你的工程支持ARC。ARC在Xcode4.2中引入,在Mac
OSXv10.6,v10.7(64位应用),iOS4,iOS5中支持,Xcode4.1中不支持这个技术.


如果你现在的工程不支持ARC技术,你可以通过一个自动转换工具来转换你的工程(工具在Edit->Convertmenu),这个工具会自动所有工程中手动管理内存的点转换成合适自动方式的(比如移除retain,release等)。这个工具会转换工程中所有的文件。当然你可以转换单个文件。


ARC提供自动内存管理的功能

ARC使得你不需要再思考何时使用retain,release,autorelease这样的函数来管理内存,它提供了自动评估内存生存期的功能,并且在编译期间自动加入合适的管理内存的方法。编译器也会自动生成dealloc函数。一般情况下,通过ARC技术,你可以不顾传统方式的内存管理方式,但是深入了解传统的内存管理是十分有必要的。

下面是一个person类的一个声明和实现,它使用了ARC技术。

@interfacePerson:NSObject

@property(nonatomic,strong)NSString*firstName;

@property(nonatomic,strong)NSString*lastName;

@property(nonatomic,strong)NSNumber*yearOfBirth;

@property(nonatomic,strong)Person*spouse;

@end


@implementationPerson

@synthesizefirstName,lastName,yearOfBirth,spouse;

@end

(有关strong的帮助,请参考“ARC
IntroducesNewLifetimeQualifiers.”)

使用ARC,你可以象下面的方式实现contrived函数:

-(void)contrived{

Person*aPerson=[[Personalloc]init];

[aPersonsetFirstName:@"William"];

[aPersonsetLastName:@"Dudney"];

[aPerson:setYearOfBirth:[[NSNumberalloc]initWithInteger:2011]];

NSLog(@"aPerson:%@",aPerson);

}

ARC管理内存,所以这里你不用担心aPerson和NSNumber的临时变量会造成内存泄漏。

你还可以象下面的方式来实现Person类中的takeLastNameFrom:方法,

-(void)takeLastNameFrom:(Person*)person{

NSString*oldLastname=[selflastName];

[selfsetLastName:[personlastName]];

NSLog(@"Lastnamechangedfrom%@to%@",oldLastname,[selflastName]);

}

ARC可以保证在NSLog调用的时候,oldLastname还存在于内存中。


ARC中的新规则

为了ARC能顺利工作,特增加如下规则,这些规则可能是为了更健壮的内存管理,也有可能为了更好的使用体验,也有可能是简化代码的编写,不论如何,请不要违反下面的规则,如果违反,将会得到一个编译期错误。

下面的这些函数:
dealloc,
retain
,
release
,
retainCount
,
autorelease。禁止任何形式调用和实现(dealloc可能会被实现),包括使用
@selector(retain)
,
@selector(release)
等的隐含调用。

你可能会实现一个和内存管理没有关系的dealloc,譬如只是为了调用
[systemClassInstance
setDelegate:nil]
,但是请不要调用
[super
dealloc]
,因为编译器会自动处理这些事情。

你不可以使用
NSAllocateObject
或者
NSDeallocateObject
.

使用alloc申请一块内存后,其他的都可以交给运行期的自动管理了。


不能在C语言中的结构中使用Objective-c中的类的指针。

请使用类类管理数据。

不能使用NSAutoreleasePool
.

作为替代,@autoreleasepool被引入,你可以使用这个效率更高的关键词。


不能使用memoryzones.

NSZone不再需要
—本来这个类已经被现代Objective-c废弃。

ARC在函数和便利变量命名上也有一些新的规定

禁止以new开头的属性变量命名。


ARCIntroducesNewLifetimeQualifiers

ARCintroducesseveralnewlifetimequalifiersforobjects,andzeroingweakreferences.Aweakreferencedoesnotextendthelifetimeoftheobjectitpointsto.Azeroingweakreferenceautomaticallybecomes
nil
if
theobjectitpointstoisdeallocated.

Youshouldtakeadvantageofthesequalifierstomanagetheobjectgraphsinyourprogram.Inparticular,ARCdoesnotguardagainststrongreferencecycles(previouslyknownasretaincycles—see“Practical
MemoryManagement”).Judicioususeofweakrelationshipswillhelptoensureyoudon’tcreatecycles.


属性变量修饰符

weak和strong两个修饰符是新引进的,使用例子如下:

//下面的作用和:@property(retain)MyClass*myObject;相同

@property(strong)MyClass*myObject;


//下面的作用和"@property(assign)MyClass*myObject;"相识

//不同的地方在于,如果MyClass的实例析构后,这个属性变量的值变成nil,而不是一个野指针,


@property(weak)MyClass*myObject;


VariableQualifiers

Youusethefollowinglifetimequalifiersforvariablesjustlikeyouwould,say,
const
.

__strong

__weak

__unsafe_unretained

__autoreleasing

__strong
is
thedefault.
__weak
specifies
azeroingweakreferencetoanobject.
__unsafe_unretained
specifies
weakreferencetoanobjectthatisnotzeroing—iftheobjectitreferencesisdeallocated,thepointerisleftdangling.Youuse
__autoreleasing
to
denoteargumentsthatarepassedbyreference(
id
*
)andareautoreleasedonreturn.

Takecarewhenusing
__weak
variables
onthestack.Considerthefollowingexample:

NSString__weak*string=[[NSStringalloc]initWithFormat:@"FirstName:%@",[selffirstName]];

NSLog(@"string:%@",string);

Although
string
is
usedaftertheinitialassignment,thereisnootherstrongreferencetothestringobjectatthetimeofassignment;itisthereforeimmediatelydeallocated.Thelogstatementshowsthat
string
has
anullvalue.

Youalsoneedtotakecarewithobjectspassedbyreference.Thefollowingcodewillwork:

NSError*error=nil;

BOOLOK=[myObjectperformOperationWithError:&error];

if(!OK){

//Reporttheerror.

//...

However,theerrordeclarationisimplicitly:

NSError*__stronge=nil;

andthemethoddeclarationwouldtypicallybe:

-(BOOL)performOperationWithError:(NSError*__autoreleasing*)error;

Thecompilerthereforerewritesthecode:

NSError__strong*error=nil;

NSError__autoreleasing*tmp=error;

BOOLOK=[myObjectperformOperationWithError:&tmp];

error=tmp;

if(!OK){

//Reporttheerror.

//...

Themismatchbetweenthelocalvariabledeclaration(
__strong
)
andtheparameter(
__autoreleasing
)
causesthecompilertocreatethetemporaryvariable.Youcangettheoriginalpointerbydeclaringtheparameter
id
__strong*
whenyoutaketheaddressofa
__strong
variable.
Alternativelyyoucandeclarethevariableas
__autoreleasing
.


UseLifetimeQualifierstoAvoidStrongReferenceCycles

Youcanuselifetimequalifierstoavoidstrongreferencecycles.Forexample,typicallyifyouhaveagraphofobjectsarrangedinaparent-childhierarchyandparentsneedtorefertotheirchildrenandviceversa,thenyoumaketheparent-to-childrelationship
strongandthechild-to-parentrelationshipweak.Othersituationsmaybemoresubtle,particularlywhentheyinvolveblock
objects.

Inmanualreferencecountingmode,
__block
idx;
hastheeffectofnotretaining
x
.
InARCmode,
__block
idx;
defaultstoretaining
x
(just
likeallothervalues).TogetthemanualreferencecountingmodebehaviorunderARC,youcoulduse
__unsafe_unretained
__blockidx;
.Asthename
__unsafe_unretained
implies,
however,havinganon-retainedvariableisdangerous(becauseitcandangle)andisthereforediscouraged.Twobetteroptionsaretoeitheruse
__weak
(if
youdon’tneedtosupportiOS4orOSXv10.6),orsetthe
__block
value
to
nil
to
breaktheretaincycle.

Thefollowingcodefragmentillustratesthisissueusingapatternthatissometimesusedinmanualreferencecounting.

MyViewController*myController=[[MyViewControlleralloc]init…];

//...

myController.completionHandler=^(NSIntegerresult){

[myControllerdismissViewControllerAnimated:YEScompletion:nil];

};

[selfpresentViewController:myControlleranimated:YEScompletion:^{

[myControllerrelease];

}];

Asdescribed,instead,youcanusea
__block
qualifier
andsetthe
myController
variable
to
nil
in
thecompletionhandler:

__blockMyViewController*myController=[[MyViewControlleralloc]init…];

//...

myController.completionHandler=^(NSIntegerresult){

[myControllerdismissViewControllerAnimated:YEScompletion:nil];

myController=nil;

};

Alternatively,youcanuseatemporary
__weak
variable.
Thefollowingexampleillustratesasimpleimplementation:

MyViewController*myController=[[MyViewControlleralloc]init…];

//...

__weakMyViewController*weakMyViewController=myController;

myController.completionHandler=^(NSIntegerresult){

[weakMyViewControllerdismissViewControllerAnimated:YEScompletion:nil];

};

Fornon-trivialcycles,however,youshoulduse:

MyViewController*myController=[[MyViewControlleralloc]init…];

//...

__weakMyViewController*weakMyController=myController;

myController.completionHandler=^(NSIntegerresult){

MyViewController*strongMyController=weakMyController;

if(strongMyController){

//...

[strongMyControllerdismissViewControllerAnimated:YEScompletion:nil];

//...

}

else{

//Probablynothing...

}

};

Insomecasesyoucanuse
__unsafe_unretained
if
theclassisn’t
__weak
compatible.
Thiscan,however,becomeimpracticalfornontrivialcyclesbecauseitcanbehardorimpossibletovalidatethatthe
__unsafe_unretained
pointer
isstillvalidandstillpointstothesameobjectinquestion.


自动释放池

使用ARC,你不能使用
NSAutoReleasePool类来管理自动释放池了,作为替代,ARC使用一个新的语法结构:


@autoreleasepool{

//Code,suchasaloopthatcreatesalargenumberoftemporaryobjects.

}

编译器根据工程是否使用ARC,来决定这个语法结构最终呈现方式,这个语法结构使用了一种比
NSAutoReleasePool
更高效的方式。


Outlets

Thepatternsfordeclaringoutletsin
iOSandOSXchangewithARCandbecomeconsistentacrossbothplatforms.Thepatternyoushouldtypicallyadopt
is:outletsshouldbe
weak
,
exceptforthosefromFile’sOwnertotop-levelobjectsinanibfile(orastoryboardscene)whichshouldbe
strong
.

Outletsthatyoucreateshouldwillthereforegenerallybe
weak
by
default:

Outletsthatyoucreateto,forexample,subviewsofaviewcontroller’svieworawindowcontroller’swindow,arearbitraryreferencesbetweenobjectsthatdonotimplyownership.

The
strong
outlets
arefrequentlyspecifiedbyframeworkclasses(forexample,
UIViewController
’s
view
outlet,
or
NSWindowController
’s
window
outlet).

Forexample:

@interfaceMyFilesOwnerClass:SuperClass


@property(weak)IBOutletMyView*viewContainerSubview;

@property(strong)IBOutletMyOtherClass*topLevelObject;

@end

Incaseswhereyoucannotcreateaweakreferencetoaninstanceofaparticularclass(suchas
NSTextView
),
youshoulduse
assign
rather
than
weak
:

@property(assign)IBOutletNSTextView*textView;

Thispatternextendstoreferencesfromacontainerviewtoitssubviewswhereyouhavetoconsidertheinternalconsistency
ofyourobjectgraph.Forexample,inthecaseofatableviewcell,outletstospecificsubviewsshouldagaintypicallybe
weak
.
Ifatableviewcontainsanimageviewandatextview,thentheseremainvalidsolongastheyaresubviewsofthetableviewcellitself.

Outletsshouldbechangedto
strong
when
theoutletshouldbeconsideredtoownthereferencedobject:

Asindicatedpreviously,thisoftenthecasewithFile’sOwner:toplevelobjectsinanibfilearefrequentlyconsideredtobeownedbytheFile’sOwner.

Youmayinsomesituationsneedanobjectfromanibfiletoexistoutsideofitsoriginalcontainer.Forexample,youmighthaveanoutletforaviewthatcanbetemporarilyremovedfromitsinitialviewhierarchyandmustthereforebemaintainedindependently.


其他的新功能

使用ARC技术,可以使得在栈上分配的指针隐式的初始化为nil,比如

-(void)myMethod{

NSString*name;

NSLog(@"name:%@",name);

}

上面的代码会Log出来一个null,不会象不使用ARC技术的时候使得程序崩溃。

转载地址:http://blog.csdn.net/nicktang/article/details/6792972
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: