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

IOS内存管理 ARC技术概述 .

2013-01-23 14:46 483 查看
转自:http://blog.csdn.net/weiqubo/article/details/8108024

ARC(AutomaticReferenceCounting )技术概述

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

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



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


概述

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

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

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

你可以使用编译标记
-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的帮助,请参考 “ARCIntroducesNewLifeti
4000
meQualifiers.”)

使用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]
,但是请不要调用
[super
dealloc]
,因为编译器会自动处理这些事情。

你不可以使用
NSAllocateObject
或者
NSDeallocateObject
.

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


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

请使用类类管理数据。

不能使用NSAutoreleasePool
.

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


不能使用memoryzones.

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

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

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


ARCIntroducesNewLifetimeQualifiers

ARCintroducesseveralnewlifetimequalifiersforobjects,and zeroingweakreferences.Aweakreferencedoesnotextendthelifetimeof
theobjectitpointsto.Azeroingweakreferenceautomaticallybecomes 
nil
 iftheobjectitpointstoisdeallocated.

Youshouldtakeadvantageofthesequalifierstomanagetheobjectgraphsinyourprogram.Inparticular,ARCdoesnotguardagainst strongreferencecycles (previously
knownasretaincycles—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
 specifiesa
zeroingweakreferencetoanobject. 
__unsafe_unretained
 specifiesweakreferencetoanobjectthatisnotzeroing—iftheobjectitreferencesisdeallocated,
thepointerisleftdangling.Youuse
__autoreleasing
 todenoteargumentsthatarepassedbyreference(
id
*
)andareautoreleasedonreturn.

Takecarewhenusing 
__weak
 variablesonthestack.Considerthefollowingexample:

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

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

Although 
string
 isusedaftertheinitialassignment,thereisnootherstrongreferencetothestringobjectatthetimeofassignment;itisthereforeimmediately
deallocated.Thelogstatementshowsthat 
string
 hasanullvalue.

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*
 whenyoutaketheaddressof
__strong
 variable.Alternativelyyoucandeclarethevariableas 
__autoreleasing
.

UseLifetimeQualifierstoAvoidStrongReferenceCycles

Youcanuselifetimequalifierstoavoidstrongreferencecycles.Forexample,typicallyifyouhaveagraphofobjectsarrangedinaparent-childhierarchyandparentsneedtorefertotheirchildrenandviceversa,thenyoumaketheparent-to-childrelationship
strongandthechild-to-parentrelationshipweak.Othersituationsmaybemoresubtle,particularlywhentheyinvolve blockobjects.

Inmanualreferencecountingmode, 
__blockidx;
 hastheeffectofnotretaining 
x
.
InARCmode, 
__blockidx;
 defaultstoretaining 
x
 (just
likeallothervalues).TogetthemanualreferencecountingmodebehaviorunderARC,youcoulduse
__unsafe_unretained__blockidx;
.Asthename 
__unsafe_unretained 
 implies,
however,havinganon-retainedvariableisdangerous(becauseitcandangle)andisthereforediscouraged.Twobetteroptionsaretoeitheruse 
__weak
 (ifyou
don’tneedtosupportiOS 4orOS X v10.6),orsetthe 
__block
 valueto 
nil
 to
breaktheretaincycle.

Thefollowingcodefragmentillustratesthisissueusingapatternthatissometimesusedinmanualreferencecounting.

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

//...

myController.completionHandler=^(NSIntegerresult){

[myControllerdismissViewControllerAnimated:YEScompletion:nil];

};

[selfpresentViewController:myControlleranimated:YEScompletion:^{

[myControllerrelease];

}];

Asdescribed,instead,youcanusea 
__block
 qualifierandsetthe 
myController
 variable
to 
nil
 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
 pointerisstillvalid
andstillpointstothesameobjectinquestion.


自动释放池

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


@autoreleasepool{

//Code,suchasaloopthatcreatesalargenumberoftemporaryobjects.

}

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


Outlets

Thepatternsfordeclaring outlets iniOSandOS XchangewithARCandbecomeconsistentacrossbothplatforms.Thepatternyoushould typically adoptis:outletsshouldbe 
weak
,
exceptforthosefromFile’sOwnertotop-levelobjectsinanibfile(orastoryboardscene)whichshouldbe 
strong
.

Outletsthatyoucreateshouldwillthereforegenerallybe 
weak
 bydefault:

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

The 
strong
 outletsarefrequentlyspecifiedbyframeworkclasses(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;

Thispatternextendstoreferencesfromacontainerviewtoitssubviewswhereyouhavetoconsiderthe internal consistencyofyourobjectgraph.Forexample,inthecaseofatableviewcell,outletstospecificsubviewsshouldagaintypically
be 
weak
.Ifatableviewcontainsanimageviewandatextview,thentheseremainvalidsolongastheyaresubviewsofthetableviewcellitself.

Outletsshouldbechangedto 
strong
 whentheoutletshouldbeconsideredtoownthereferencedobject:

Asindicatedpreviously,thisoftenthecasewithFile’sOwner:toplevelobjectsinanibfilearefrequentlyconsideredtobeownedbytheFile’sOwner.

Youmayinsomesituationsneedanobjectfromanibfiletoexistoutsideofitsoriginalcontainer.Forexample,youmighthaveanoutletforaviewthatcanbetemporarilyremovedfromitsinitialviewhierarchyandmustthereforebemaintainedindependently.


其他的新功能

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

-(void)myMethod{

NSString*name;

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

}

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

ARC工作原理是在编译程序的时候由xCode将内存操作的代码(如:retain,release和autorelease)自动添加到需要的位置。

ARC只能在iOS4和iOS5上使用,weakrefrences只能在iOS5上使用,并且只能是工程在ARC管理内存的时候才能用。

老版本的工程是可以转换成使用ARC的工程,转换规则包括:

      1.去掉所有的retain,release,autorelease

      2.把NSAutoRelease替换成@autoreleasepool{}块

      3.把assign的属性变为weak

使用ARC的一些强制规定

      1.不能直接调用dealloc方法,不能调用retain,release,autorelease,reraubCount方法,包括@selector(retain)的方式也不行

      2.截图租户事故宣布dealloc方法来管理一些资源,但不能用来释放实例变量,也不能在dealloc方法里面去掉[superdealloc]方法,在ARC下父类的dealloc同样由编译器来自动完成

      3.CoreFoundation类型的对象任然可以用CFRetain,CFRelease这些方法

      4.不能在使用NSAllocateObject和NSDeallocateObject对象

      5.不能在c结构体中使用对象指针,如果有类似功能可以创建一个Objective-c类来管理这些对象

      6.在id和void*之间没有简便的转换方法,同样在Objective-c和coreFoundation类型之间的转换都需要使用编译器制定的转换函数

      7.不能再使用NSAutoreleasePool对象,ARC提供了@autoreleasepool块来代替它,这样更加有效率

      8.不能使用内存存储区(不能再使用NSZone)

      9.不能以new为开头给一个属性命名

      10.声明outlet时一般应当使用weak,除了对StoryBoard这样nib中间的顶层对象要用strong

      11.weak相当于老版本的assign,strong相当于retain

对工程中的单个文件制定不使用ARC的方法:在targets的buildphases选项下CompileSources下选择要不使用arc编译的文件,双击它,输入
-fno-objc-arc
即可
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: