此文章由Tom翻译,首发于csdn的blog转自:http://blog.csdn.net/nicktang/article/details/6792972
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-FreeBridgedTypes”);ARCalsointroducesnewlifetimequalifiersforobjectreferencesanddeclaredproperties.
你可以使用编译标记-fobjc-arc来让你的工程支持ARC。ARC在Xcode4.2中引入,在MacOSXv10.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的帮助,请参考“ARCIntroducesNewLifetimeQualifiers.”)
使用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,譬如只是为了调用[systemClassInstancesetDelegate:nil]
,但是请不要调用[superdealloc]
,因为编译器会自动处理这些事情。
你不可以使用NSAllocateObject
或者
NSDeallocateObject
.
使用alloc申请一块内存后,其他的都可以交给运行期的自动管理了。
不能在C语言中的结构中使用Objective-c中的类的指针。
请使用类类管理数据。
不能使用NSAutoreleasePool
.
作为替代,@autoreleasepool被引入,你可以使用这个效率更高的关键词。
不能使用memoryzones.
NSZone不再需要
—本来这个类已经被现代Objective-c废弃。
ARC在函数和便利变量命名上也有一些新的规定
禁止以new开头的属性变量命名。
ARCIntroducesNewLifetimeQualifiers
ARCintroducesseveralnewlifetimequalifiersforobjects,andzeroingweakreferences.Aweakreferencedoesnotextendthelifetimeoftheobjectitpointsto.Azeroingweakreferenceautomaticallybecomesnil
iftheobjectitpointstoisdeallocated.
Youshouldtakeadvantageofthesequalifierstomanagetheobjectgraphsinyourprogram.Inparticular,ARCdoesnotguardagainststrongreferencecycles(previouslyknownasretaincycles—see“PracticalMemoryManagement”).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
isthedefault.__weak
specifiesazeroingweakreferencetoanobject.__unsafe_unretained
specifiesweakreferencetoanobjectthatisnotzeroing—iftheobjectitreferencesisdeallocated,thepointerisleftdangling.Youuse__autoreleasing
todenoteargumentsthatarepassedbyreference(id*
)andareautoreleasedonreturn.
Takecarewhenusing__weak
variablesonthestack.Considerthefollowingexample:
NSString__weak*string=[[NSStringalloc]initWithFormat:@"FirstName:%@",[selffirstName]];
|
NSLog(@"string:%@",string);
|
Althoughstring
isusedaftertheinitialassignment,thereisnootherstrongreferencetothestringobjectatthetimeofassignment;itisthereforeimmediatelydeallocated.Thelogstatementshowsthatstring
hasanullvalue.
Youalsoneedtotakecarewithobjectspassedbyreference.Thefollowingcodewillwork:
NSError*error=nil;
|
BOOLOK=[myObjectperformOperationWithError:&error];
|
if(!OK){
|
//Reporttheerror.
|
//...
|
However,theerrordeclarationisimplicitly:
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.Youcangettheoriginalpointerbydeclaringtheparameterid__strong*
whenyoutaketheaddressofa__strong
variable.Alternativelyyoucandeclarethevariableas__autoreleasing
.
UseLifetimeQualifierstoAvoidStrongReferenceCycles
Youcanuselifetimequalifierstoavoidstrongreferencecycles.Forexample,typicallyifyouhaveagraphofobjectsarrangedinaparent-childhierarchyandparentsneedtorefertotheirchildrenandviceversa,thenyoumaketheparent-to-childrelationshipstrongandthechild-to-parentrelationshipweak.Othersituationsmaybemoresubtle,particularlywhentheyinvolveblockobjects.
Inmanualreferencecountingmode,__blockidx;
hastheeffectofnotretainingx
.InARCmode,__blockidx;
defaultstoretainingx
(justlikeallothervalues).TogetthemanualreferencecountingmodebehaviorunderARC,youcoulduse__unsafe_unretained__blockidx;
.Asthename__unsafe_unretained
implies,however,havinganon-retainedvariableisdangerous(becauseitcandangle)andisthereforediscouraged.Twobetteroptionsaretoeitheruse__weak
(ifyoudon’tneedtosupportiOS4orOSXv10.6),orsetthe__block
valuetonil
tobreaktheretaincycle.
Thefollowingcodefragmentillustratesthisissueusingapatternthatissometimesusedinmanualreferencecounting.
MyViewController*myController=[[MyViewControlleralloc]init…];
|
//...
|
myController.completionHandler=^(NSIntegerresult){
|
[myControllerdismissViewControllerAnimated:YEScompletion:nil];
|
};
|
[selfpresentViewController:myControlleranimated:YEScompletion:^{
|
[myControllerrelease];
|
}];
|
Asdescribed,instead,youcanusea__block
qualifierandsetthemyController
variabletonil
inthecompletionhandler:
__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
iftheclassisn’t__weak
compatible.Thiscan,however,becomeimpracticalfornontrivialcyclesbecauseitcanbehardorimpossibletovalidatethatthe__unsafe_unretained
pointerisstillvalidandstillpointstothesameobjectinquestion.
自动释放池
使用ARC,你不能使用NSAutoReleasePool类来管理自动释放池了,作为替代,ARC使用一个新的语法结构:
@autoreleasepool{
|
//Code,suchasaloopthatcreatesalargenumberoftemporaryobjects.
|
}
|
编译器根据工程是否使用ARC,来决定这个语法结构最终呈现方式,这个语法结构使用了一种比NSAutoReleasePool
更高效的方式。
Outlets
ThepatternsfordeclaringoutletsiniOSandOSXchangewithARCandbecomeconsistentacrossbothplatforms.Thepatternyoushouldtypicallyadoptis:outletsshouldbeweak
,exceptforthosefromFile’sOwnertotop-levelobjectsinanibfile(orastoryboardscene)whichshouldbestrong
.
Outletsthatyoucreateshouldwillthereforegenerallybeweak
bydefault:
Outletsthatyoucreateto,forexample,subviewsofaviewcontroller’svieworawindowcontroller’swindow,arearbitraryreferencesbetweenobjectsthatdonotimplyownership.
Thestrong
outletsarefrequentlyspecifiedbyframeworkclasses(forexample,UIViewController
’sview
outlet,orNSWindowController
’swindow
outlet).
Forexample:
@interfaceMyFilesOwnerClass:SuperClass
|
|
@property(weak)IBOutletMyView*viewContainerSubview;
|
@property(strong)IBOutletMyOtherClass*topLevelObject;
|
@end
|
Incaseswhereyoucannotcreateaweakreferencetoaninstanceofaparticularclass(suchasNSTextView
),youshoulduseassign
ratherthanweak
:
@property(assign)IBOutletNSTextView*textView;
|
Thispatternextendstoreferencesfromacontainerviewtoitssubviewswhereyouhavetoconsidertheinternalconsistencyofyourobjectgraph.Forexample,inthecaseofatableviewcell,outletstospecificsubviewsshouldagaintypicallybeweak
.Ifatableviewcontainsanimageviewandatextview,thentheseremainvalidsolongastheyaresubviewsofthetableviewcellitself.
Outletsshouldbechangedtostrong
whentheoutletshouldbeconsideredtoownthereferencedobject:
Asindicatedpreviously,thisoftenthecasewithFile’sOwner:toplevelobjectsinanibfilearefrequentlyconsideredtobeownedbytheFile’sOwner.
Youmayinsomesituationsneedanobjectfromanibfiletoexistoutsideofitsoriginalcontainer.Forexample,youmighthaveanoutletforaviewthatcanbetemporarilyremovedfromitsinitialviewhierarchyandmustthereforebemaintainedindependently.
其他的新功能
使用ARC技术,可以使得在栈上分配的指针隐式的初始化为nil,比如
-(void)myMethod{
|
NSString*name;
|
NSLog(@"name:%@",name);
|
}
|
上面的代码会Log出来一个null,不会象不使用ARC技术的时候使得程序崩溃。