OC – ARC(自动引用计数)

1.哟是机关引用计数?

顾明思义,自动引用计数(ARC,Automatic Reference
Counting)是依赖内存管理遭针对援采取自动计数的技巧。

在OC中使用ARC机制,让编译器来进展内存管理。在新一代apple
LLVM编译器中安装ARC为有效状态,就凭需更键入retain或者release代码,这在降程序崩溃、内存泄露等高风险的而,很挺程度达到减小了支出顺序的工作量。编译器完全了解目标对象,并能这释放那些休以吃使用的靶子。

1.2 内存管理/引用计数

1.2.1 概要

OC中之内存管理,也尽管是引用计数。可以据此开关房间的对等为例来说明引用计数的编制,比如:上班上办公室用照明,下班离开办公不需照明。

只要办公室里之照明设备只发一个。上班上办公的人欲照明,所以只要把灯打开,而对于下班离开办公室的丁吧,已经不欲照明了,所以要拿灯关掉。若是很多丁上下班,每个人还开灯或关灯,那么办公室的情又拿如何呢?最早下班离开的人一旦关了灯,办公室里还不曾挪动之丁都用处相同片黑暗中游。

解决这个题材的点子是要办公室于还有至少1人的气象下维持开灯状态,而以管人常常保持关灯状态。

a.第一私进入办公,“需要照明的总人口”加1,计数值从0变成了1,因此要起来灯。

b.之后于有人进来办公,”需要照明的人口”就加1,计数值从1变为2.

c.于有人下班离开办公室,”需要照明的食指”就减1。如计量数值从2改为1.

d.最后一个总人口下班离开办公时,”需要照明的人口”减1,计数值从1变成了0,因此要关灯。

针对办公室照明设备所开的动作与针对性OC对象所做的动作之对比。

针对照明设备所召开的动作:开灯 – 需要照明 – 不欲照明 – 关灯

针对OC对象所举行的动作:  生成对象 – 持有对象 – 释放对象 – 废弃对象。

利用计数功能计算需要照明的人头,使办公室的照明得到了非常好的田间管理。同样,使用引用计数功能,对象呢便能够获取特别好之保管,这就是是OC的内存管理。

C语言 1

1.2.2 内存管理之沉思方式

温馨变的目标,自己所兼有。

不友好变的目标,自己呢克有。

匪以待好具有的对象时释放。

未自己独具的靶子无法自由。

//  对应的方

目标操作:生成并保有对象 – 持有对象 – 释放对象 – 废弃对象

OC方法  :alloc/new/copy/mutablecopy – retain – release – dealloc

这些关于OC内存管理的不二法门,实际上不包括以该语言中,而是包含在cocoa框架中,用于ios开发。cocoa框架中Foundation框架类库的NSObject类担负内存管理之任务。OC内存管理中的alloc/retain/release/dealloc方法分别代表NSObject类的alloc类方法、retain实例方法、release实例方法及dealloc实例方法。

协调别对象,自己所具有

使alloc new copy mutableCopy
开头的艺术名代表自己变的目标只有自己抱有。本文所说的“自己”固然对应前文提到的“对象的利用环境”,但拿的理解为编程人员“自身”也是无错的。下面写有了好别并负有对象的代码,为变化并具备对象,我们应用alloc
方法。

//    自己生成并持有对象
id obj = [[NSObject alloc] init];

//   使用NSObject类的alloc类方法就能自己生成并持有对象。指向生成并持有对象的指针被赋给变量obj,另外,使用如下new类方法也能生成并持有对象。
//  自己生成并持有对象
id obj = [NSObject new];

copy
方法以基于NSCopying方法约定,由各实现的copyWithZone,方法变并拥有对象的副本。与copy方法类似,mutableCopy方法以基于NSMutableCopying方法约定,由各实现之mutableCopyWithZone方法变并富有对象的副本。两者的界别在,copy方法生成不可变更的对象,而mutableCopy方法生成可变更的目标。这看似于NSArray和NSMutableArray的差别。

allocMyObject newThatObject copyThis mutableCopyYourObject
方法呢表示和谐变并装有对象。

只是allocate newer copying mutableCopyed 并无属同一类别的方法。

非自己别的目标,自己呢会有所 

之所以上述项目之外的方式得到的对象,即用alloc/new/copy/mutableCopy
以外的法子赢得的目标,因为无友好变并有所,所以自己非是欠目标的所有者。我们来行使NSMutableArray类的array类方法。

//    取得非自己生成并持有的对象
id obj = [NSMutableArray array];

//    取得的对象存在,但是自己不持有对象,源代码中,NSMutableArray类对象被赋值给obj,但变量obj自己并不持有该对象,使用retain方法可以持有对象。

//    取得非自己生成并持有的对象,取得的对象虽在,但是自己并不持有
id obj = [NSMutableArray array];

//    自己持有对象,通过retain方法,非自己生成的对象跟用alloc/new/copy/mutableCopy方法生成并持有的对象一样,成为了自己所持有的。
[obj retain]

匪待协调有所的对象时放

温馨装有的靶子,一旦不再需要,持有者有义务释放该对象,释放使release方法。 

//    自己生成并持有对象
id obj = [[NSObject alloc] init];

//    释放对象,指向对象的指针仍然被保留在变量obj中,貌似能够访问。但是对象一经释放绝对不可以访问。

如此,用alloc方法由自己生成并持有的对象就通过release方法释放了,自己生成而非自己所持有的对象,若用retain方法变为自己持有,也同样可以用release方法释放。

//    取得非自己生成并持有的对象
id obj = [NSMutableArray array]

//    自己持有对象
[obj retain]

//    自己释放对象,对象不可再被访问
[obj release]

 如果假定为此有方法变对象,并拿该返还给该方式的调用方,那么他的源代码又是安的啊?

//    如下代码,原封不动地返回用alloc方法生成并持有的对象,就能让调用方也持有该对象。allocObject 名称符合命名规则,因此它与用alloc方法生成并持有对象的情况完全相同,所以使用allocObject方法也就意味着“自己生成并持有对象”

- (id)allocObject{
   //    自己生成并持有对象
   id obj = [[NSObject alloc] init];
   //     自己持有对象
   return obj;
}

//  调用[NSMutableArray array] 方法使取得的对象存在,但自己不持有对象,又是如何实现的?根据上文的命名规则,不能使用以alloc/new/copy/mutableCopy开头的方法名,因此要使用object这个方法名。

- (id)object{
    id obj = [[NSObject alloc] init];
    [obj autorelease];
    return obj;  
}

//    上例中,我们使用了autorelease方法,用该方法,可以使取得的对象存在,但自己不持有对象。

 autorelease
提供这么的效益,使对象在超过指定的活范围时能活动并正确的刑释解教(调用release方法)下面是release和autorelease的分

C语言 2

没辙自由非友好具有的靶子

对于非alloc/new/copy/mutableCopy方法变并有着的目标,或是用retain方法有的靶子,由于持有者是投机,所以当不需要该对象时得用那出狱。而经过外界所获的靶子绝对不可知放。倘若在应用程序中释放了不友好所负有的目标就会导致倒。例如自己变并具备对象后,在自由了不再徐亚ode对象下再行出狱。

//    自己生成并持有对象
id obj = [[NSObject alloc] init];

//    对象已释放
[obj release];

//    释放之后再次释放已非自己持有的对象,应用程序崩溃,崩溃情况,再度废弃已经废弃了的对象时崩溃,访问已经废弃的对象时崩溃。
----------------------------------------------------------------------------------------------------------

//    取得的对象存在,但自己不持有对象
id objq = [obj0 object]

//    释放了非自己持有的对象,这肯定会导致应用程序崩溃

 如这些事例所示,释放非友好有的目标见面招致程序崩溃。因此绝对不要失去自由非友好所有的对象。

1.2.3 alloc/retain/release/dealloc 实现

寓NSObject类的Foundation框架并没有明白,不过,Foundation框架下的Core
Foundation框架的源代码,以及经调用NSObject类进行内存管理有的源代码是堂而皇之之。但是,没有NSObject类的源代码,就老不便了解NSObject类的里贯彻细节,所以,我们第一使开源软件GNUstep来说明。

GNUstep是Cocoa框架的换框架,也就是说,GUNstep的源代码虽未能够说与Cocoa实现完全相同,但是于使用者角度来拘禁,两者的所作所为以及促成方式是一致的,或者说老相像,

id obj = [NSObject alloc];

//    这里调用了NSObject类的alloc类方法在NSObject.m源代码中的实现如下。

GNUstep/modules/core/base/Source/NSObject.m alloc

+(id)alloc{
    return [self allocWithZone: NSDefaultMallocZone( )];
}

+(id)allocWithZone: (NSZone *)z{
    return NSAllocateObject (self, 0 , z);
}

//    通过allocWithZone类方法调用NSAllocateObject函数分配了对象,下面我们看看NSAllocateObject函数。

GNUstep/modules/core/base/Source/NSObject.m  NSAllocateObject

struct obj_layout{
    NSUInteger retained;
}

inline id
NSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone){
    int size = 计算容纳对象所需内存大小
    id new = NSZoneMalloc(zone,size);
    memset(new, 0, size);
    new = (id)& ((struct obj_layout *)new)[1];  
}

//    NSAllocateObject 函数通过调用NSZoneMalloc函数来分配存放对象所需的内存空间,之后将该内存空间置为0,最后返回作为对象而使用的指针。

 注:NSDefaultMallocZone,NSZoneMalloc等称号被寓的NSZone是呀也?它是为以防内存碎片化而引入的结构,对内存分配的区域自进行多重化管理,根据使用对象的目的、对象的轻重分配内存,从而加强了内存管理的频率。

然而,如同苹果官方文档中所说,现在的运行时系统只是简短地忽视的区域的定义,运行时系统遭到之内存管理自己既尽有效率,使用区域来管理内存反而会挑起内存以频率低下和源代码复杂化等问题。

下面是失去丢NSZone后简化了底源代码:

GNUstep/modules/core/base/Source/NSObject.m alloc 简化版

struct obj_layout {
    NSUInteger retained;
}

+ (id) alloc{
    int size = sizeof (struct obj_layout) + 对象大小
    struct obj_layout * p = (struct obj_layout *) calloc (1,size);
    return (id) (p+1)
}

 alloc类方法用struct
obj_layout中的retained整数来保存引用计数,并以那描绘副对象内存头部,该对象内存块全部置0后归。

对象的援计数可经过retainCount实例方法得到

id obj = [[NSObject alloc] init];
NSLog(@"retainCount = %d",[objc retainCount]);

//    显示retainCount = 1
执行alloc后对象的retainCount 是 “1”,下面通过GNUstep的源代码来确认。

- (NSUInteger) retainCount{
   return NSExtraRefCount (self) + 1;
}

inline NSUInteger
NSExtraRefCount (id anObject){
    return ((struct obj_layout *) anObject) [-1].retained;
}

1.2.4 苹果的贯彻

以NSObject
类的alloc类方法齐设置断点,追踪程序的实施,以下列出了行所调用的章程及函数。

+alloc

+allocWithZone

class_createInstance

calloc

alloc类方法首先调用allocWithZone类方法,这和GNUstep的兑现同,然后调用class_createInstance函数,最后经调用calloc来分配内存块。这和前的GNUstep的兑现并无多雅距离。

retainCount/retain/release实例方法以是怎落实的啊?下面列出各个艺术分别调用的点子阿和函数

-retainCount

_CFDoExternRefOperation

CFBasicHashGetCountOfKey

 

-retain

_CFDoexternRefOperation

CFBasicHashAddValue

 

-release 

_CFDoExternRefOperation

CFBasicHashRemoveValue

(CFBasicHashRemoveValue 返回0 时,-release调用dealloc)

梯次艺术都由此与一个调用了_CFDoExternRefOperation函数,调用了千篇一律名目繁多名称相似之函数。如这些函数称呼的前缀”CF”所示,它们含有于Core
Foundation框架源代码中,即是CFRuntime.c的_CFDoExternRefOperation函数。

1.2.5 autorelease

autorelease
就是半自动释放,这看起来很像ARC,但实质上它们重仿佛于C语言中活动变量(局部变量)的风味。

当C语言中,程序执行时,如果某个机关变量超出其作用域,该机关变量将让活动抛弃。

{
   int a;
}

//    因为超出变量的作用域,自动变量 int a将被废弃,不可再访问。

 

autorelease会像C语言的全自动变量那样来比对象实例。当过该犯用域(相当给变量作用域)时,对象实例的release实例方法让调用。另外,同C语言的电动变量不同的是,编程人员可以设定变量的作用域。

autorelease的有血有肉行使方法如下:

(1)生成并装有NSAutoreleasePool 对象。

(2)调用已分配对象的autorelease实例方法

(3)废弃NSAutoreleasePool对象。

C语言 3

NSAutoreleasePool对象的生活周期相当给C语言变量的作用域。对于持有调用过autorelease实例方法的靶子,在抛开NSAutoreleasePool对象时,都拿调用release实例方法。

源代码如下:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain]

//    这里的[pool drain] 等同于 “[obj release]”

 在Cocoa框架中,相当给次主循环的NSRunLoop或者当另外程序可运行的地方,对NSAutoreleasePool对象进行转变,持有与废弃处理。因此,应用程序开发者不肯定不得用该对象开展开工作。

C语言 4

虽说,但当大方发生autorelease的靶子时,只要不弃NSAutoreleasePool对象,那么生成的目标就是无克叫假释,因此有时会生内存不足的光景。典型的事例就是读取大量图像的以转该尺寸。图像文件读入到NSData对象,并从中生成UIImage对象,改变该对象尺寸后生成新的UIImage对象,这种场面下,就见面大量发生autorelease的目标。

在这个状下起必不可少当当的地方,生成,持有还是丢弃Pool对象。

一般而言在动用OC,也不怕是Foundation框架时,无论调用啦一个目标的autorelease实例方法,实现达标是调用的还是NSObject类的autorelease实例方法。但是对NSAutoreleasePool类,atorelease实例方法就让该类重载,因此运行时就见面出错。

1.3 ARC规则

1.3.1 概要

事实上“引用计数式”内存管理的面目部分于ARC中并无变动,就像
“自动引用计数”这个称谓表示的那么,ARC只是半自动的助我们处理“引用计数”的相关部分。

对某文件可卜采取或者不行使ARC.

1.3.2 内存管理之思量方式

团结变的靶子,自己有着

莫友好变的目标,自己也能有所

和谐有的对象不再需要经常放

非友好所有的目标无法自由

1.3.3 所有权修饰符

OC编程中为了处理目标,可拿变量类型定义为id类型或各种对象类型。

所谓目标类型就是依靠为NSObject这样的OC类的指针,如 “NSObject
*”。id类型用于隐藏对象类型的类名部分,相当给C语言中常用之 “void *”

ARC有效时,id类型和目标类型通C语言其它品类不同,其种类及得叠加所有权修饰符。

__strong __weak __unsafe_unretained __autoreleasing

__strong 修饰符

__strong
修饰符是id类型和目标类型默认的所有权修饰符。也就是说,以下源代码中之id变量,实际上给增大了所有权修饰符。

id obj = [[NSObject alloc] init];

id
和目标类型在未曾明白指定所有权修饰符时,默认为__strong修饰符。上面的代码和以下相同。

id __strong obj = [[NSObject alloc] init];

该源代码再ARC无效的上还要欠怎么表达为?

//    ARC无效
id obj = [[NSObject alloc] init];

//    该源代码一看则名,目前在表面上没有任何变化,在看看下面的代码
{
    id __strong obj = [[NSObject alloc] init];
}

此源代码明确指定了C语言的变量的作用于。ARC无效的时候,该源代码可记述如下:
//   ARC无效
{
    id obj = [[NSObject alloc] init];
    [obj release];
}

 为了释放生成并有着的对象,增加了调用release方法的代码。该源代码进行的动作以及先前ARC有效时的动作了同。

这般源代码所示,附有__strong修饰符的变量obj在超其变量作用域时,即在拖欠变量被撇下时,会放其给给予的对象。

苟 “strong” 这个称号所示,__strong
修饰符表示针对目标的”强引用”。持有强引用的变量在超其意图域时被抛,随着大引用的失灵,引用的靶子见面随着释放。

下关注一下源代码中有关目标的所有者的片

{
   id __strong obj = [[NSObject alloc] init];
}

//    此源代码就是之前自己生成并持有对象的源代码,该对象的所有者如下:

{
    //    自己生成并持有对象
   id __strong obj = [[NSObject alloc] init]

    //    因为变量obj为强引用,所以自己持有该对象
}

//    因为变量obj超出其作用域,强引用失效,所以自动地释放自己持有的对象,对象的所有者不存在,因此废弃该对象。

 此处,对象的主人与目标的生存周期是举世瞩目的,那么,在获取不自己别并有着的对象时以见面如何为?

//    在NSMutableArray类的array类方法的源代码中取得非自己生成并持有的对象,具体如下

{  
     //    取得非自己生成并持有的对象    
     id __strong obj = [NSMutableArray array];

     //     因为变量obj为强引用,所以自己持有对象
}

//    因为变量obj超出其作用域,强引用失效,所以自动地释放自己持有的对象

在这里对象的所有者和对象的生存周期也是明确的

{
    //    自己生成并持有对象
    id __strong obj = [[NSObject alloc] init];
    //    因为变量obj为强引用,所以自己持有对象

}

 //    因为变量obj超出其作用域,强引用失效,所以自动地释放自己持有的对象,对象的所有者不存在,因此废弃该对象。

 当然,附有__strong 修饰符的变量之间可以互相赋值。

id __strong obj0 = [[NSObject alloc] init]; // 对象A
//    obj0持有对象A的强引用

id __strong obj1 = [[NSObject alloc] init]; //   对象B
//    obj1持有对象B的强引用

id __strong obj2 = nil;
//    obj2不持有任何对象

obj0 = obj1;
//    obj0持有由obj1赋值的对象B的强引用,因为obj0被赋值,所以原先持有的对对象A的强引用时效,对象A的所有者不存在,因此废弃对象A.
此时,持有对象B的强引用的变量为 obj0 和 obj1

obj2 = obj0;
//    obj2 持有obj0赋值的对象B的强引用,此时持有对象B的强引用的变量为obj0,obj1,obj2

obj1 = nil;
此时持有对象B的强引用的变量为obj0,obj2

obj2 = nil;
此时持有对象B的强引用的变量为obj0

obj3 = nil;
此时持有对象B的强引用的变量没有,所以废弃对象B

 __weak 修饰符

星星单目标循环引用的例子。

//    ARC下 对象的权限修饰符是__strong

@interface Test : NSObject 
{
     id __strong _obj;
}

- (void)setObject:(id __strong) _obj;
@end

@implementation Test
- (id)init
{
   self = [super init];
   return self;
}

- (void)setObject:(id __strong)obj
{
   _obj = obj;
}

//    以下为循环引用。
{
   // test0 持有对象a的强引用
   id test0 = [[Test alloc] init]; // 对象a
   // test1 持有对象b的强引用
   id test1 = [[Test alloc] init]; // 对象b
   // 对象a的obj成员变量持有对象b的强引用
   [test0 setObject:test1];
   //  对象b的obj成员变量持有对象a的强引用
   [test1 setObject:test0];
}

//    因为test0变量超出其作用域,强引用失效,所以释放对象a
//    因为test1变量超出其作用域,强引用失效,所以释放对象b
//    此时持有对象a的强引用的变量为对象b的obj
//    此时持有对象b的强引用的变量为对象a的obj
发生内存泄露

//
循环引用容易有内存泄露,所谓内存泄露就是理所应当废弃之目标在过量该生活周期后持续存在。此代码的本心是给变量test0的目标a和对象b在超越其变量作用域时被放走,即以对象不吃其他变量持有的状态下予以抛弃。但是,循环引用使得对象不可知让再度丢掉。

例如下这种情形,虽然光发生一个对象,但于该对象持有其自身时,也会见发生循环引用(自援)

id test = [[Test alloc] init];

[test setObject:test];

安才会免循环引用呢?看到__strong修饰符就见面发觉及了,既然发生strong,就应当出同之对应的weak,也就是说,使用__weak修饰符可以避免循环引用。

__weak修饰符与__strong修饰符相反,提供弱引用,弱引用不能够享有对象实例。

id __weak obj = [[NSObject alloc] init];
变量附加上了 __weak 修饰符,实际上如果编译以下代码,编译器会发出警告

warning: assigning retained obj to weak variable; obj will be released after assignment [-Warc-unsafe-retained-assign]

 此源代码用协调变并有所的靶子赋值给副__weak修饰符的变量obj,即变量obj持有对象有对象的弱引用。因此,为了不为团结有所的状态来保存自己别并具有的目标,生成的目标见面这吃释放。编译器对之见面让起警示。如果如下这样,将目标赋值给副__strong
修饰符的变量之后还赋值给副__weak 修饰符的变量,就无会见起警告了。

{
  // 自己生成并持有对象
  id __strong obj0 = [[NSObject alloc] init];
  // 因为obj0变量为强引用,所以自己持有对象,obj1变量持有生成对象的弱引用    
  id __ weak obj1 = obj0;
}
//    因为obj0变量超出其作用域,强引用失效
      所以自动释放自己持有的对象
      因为对象的所有者不存在,所以废弃该对象

 因为带__weak
修饰符的变量(即死引用)不享有对象,所以在盖该变量作用域时,对象就是于放出使像下这样将以前恐发生循环引用的近乎成员变量改化附有__weak修饰符的积极分子变量的话,该现象可以避免。

@interface Test : NSObject
{
   id  __weak obj;
}
- (void)setObject:(id __strong)obj;
@end

//    互相弱引用

对象a             ---->    对象b

id __weak obj    <----    id __weak obj

 __weak修饰符还有另外一个优点,在备某目标的凋谢引用的时段,若该对象吃抛弃,这个弱引用也会受置为nil

id __weak obj0 = nil;
{
   id obj1 = [[NSObject alloc] init]; //    对象a
   obj0 = obj1;
}

//    对象a出了作用域被释放掉,所以弱引用obj0也会被置为nil

 像这样,使用__weak可以避循环引用。

 __unsafe_unretained 修饰符

__unsafe_unretained
修饰符正而该名unsafe所示,是勿安全之所有权修饰符。尽管ARC式的内存管理是编译器的行事,但从__unsafe_unretained修饰符的变量不属编译器的内存管理之靶子,这同碰在运用的时候要留意。

id __unsafe_unretained obj = [[NSObject alloc] init];

//    该源代码将自己生成并持有的对象赋值给附有__unsafe_unretained修饰符的变量中。虽然使用了 unsafe 的变量,但是编译器不会忽略,而是给出适当的警告。

warning:assignin retained obj to unsafe_unretained variable;obj will be released after assignment

 附有 __unsafe_unretained 修饰符的变量和附有__weak
修饰符的变量一样,因为自己别并负有的靶子不能够连续为和谐有所,所以生成的目标会这让放出。到此处,__unsafe_unretained修饰符和__weak修饰符是一致的,下面我们看下源代码的别:

id __unsafe_unretained obj1 = nil;
{
   // 自己生成并持有对象,因为obj0变量为强引用,所以自己持有对象
   id __strong obj0 = [[NSObject alloc] init];
  // 虽然obj0变量赋值给obj1,但是obj1变量既不持有对象的强引用,也不持有弱引用
   obj1 = obj0;
   NSLog(@"A: %@",obj1);
}
// 因为obj0变量超出其作用域,强引用失效,所以自动释放自己持有的对象。因为对象无持有者所以废弃该对象。
// 输出obj1 变量表示的对象  obj1变量表示的对象已经被废弃(悬垂指针) 错误访问
NSLog(@"B: %@",obj1);

//    执行的结果为

A : <NSObject :0x753e180>
B : <NSObject :0x753e180>

也就是说,最后一行的NSLog只是正正常运作而已。虽然聘了已经于抛的目标,但是应用程序在个别运行状况下才会倒。

再试用__unsafe_unretained修饰符时,赋值给从__strong修饰符的变量时有必不可少确保让赋值的目标真正有。

缘何用采取下__unsafe_unretained修饰符的变量?

按当iOS4以及 OS X Snow Leopard 的应用程序中,必须使
__unsafe_unretained修饰符来替代__weak修饰符,赋值给附有
__safe_unretained
修饰符变量的靶子在经该变量使用时,如果没管该确是,那么应用程序就会崩溃。 

__autoreleasing 修饰符

ARC有效时未克采取autorelease方法,另外也未可知使用NSAutoreleasePool类。这样一来,虽然autorelease无法直接运用,但实质上ARC有效时autorelease功能是自从作用的。

// ARC无效

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

id obj = [[NSObject alloc] init];

[obj autorelease];

[pool drain];

// ARC有效的时候,该源代码也能写成下面这样

@autoreleasepool{

    id __autoreleasing obj = [[NSObject alloc] init];

}

 指定“@autoreleasepool片”来取代“NSAutoreleasePool类对象生成、持有与抛”这无异于限量。

此外,ARC有效时,要透过将目标赋值给附加了__autoreleasing
修饰符的变量来顶替调用autorelease方法。对象赋值给从__autoreleasing
修饰符的变量等价于在ARC无效时调用对象的autorelease方法,即对象为注册及autoreleasepool

不过即可以领略吧,在ARC有效时,用@autoreleasepool片替代NSAutoreleasePool类,用从__autoreleasing修饰符的变量替代autorelease方法。

C语言 5

而,显示地附加__autoreleasing修饰符同显式的叠加__strong修饰符一样罕见。

咱们经过实例看看为什么非显示的下__autoreleasing修饰符也可。

赢得不自己别并富有的目标时,如同坐下源代码,虽然好行使alloc/new/copy/mutableCopy以外的章程来收获对象,但该目标就让注册到了autoreleasepool。这和以ARC无效时收获调用了autorelease方法的目标是一模一样的。这是出于编译器会检查方式名是否以alloc/new/copy/mutableCopy开始,如果不是则自动将返回值的目标注册到autoreleasepool.init方法返回值的靶子非会见报及autoreleasepool。

@autoreleasepool {

//  取得非自己生成并持有的对象

id __strong obj = [NSMutableArray array];

//  因为变量obj为强引用,所以自己持有对象,并且该对象由编译器判断其方法名后自动注册到autoreleasepool       

}

// 因为变量obj超出其作用域,强引用失效,所以自动释放自己持有的对象。
同时,随着@autoreleasepool块的结束,注册到autoreleasepool中的所有对象被自动释放,
因为对象的所有者不存在,所以废弃对象

 像这样,不适用__autoreleasing修饰符也能要对象注册及autoreleasepool.以下为获取不友好别并持有对象时受调用方法的源代码实例。

+ (id) array{
    return [[NSMutableArray alloc] init];
}

//    该代码没有使用__autoreleaseing修饰符,可写成以下形式

+ (id) array{
    id obj = [[NSMutableArray alloc] init];
    return obj;
}

 因为没有显得指定所有权修饰符,所以id obj同附有__strong 修饰符的 id
__strong
obj是意一样的。由于return使得对象变量超出该犯用域,所以该强引用对应的和睦装有的目标会叫电动释放,但拖欠目标作为函数的归来值,编译器会活动将该注册及autoreleasepool。

以下为使__weak修饰符的例子。虽然__weak修饰符是为避免循环引用而使用的,但于访问附有__weak修饰符的变量时,实际上必定使顾注册及autoreleasepool的目标。

id __weak obj1 = obj0;
NSLog(@"class = %@", [obj1 class]);

// 以下源代码与此相同

id __weak obj1 = obj0;

id __autoreleasing tmp = obj1;

NSL(@"class = %@", [tmp class]);

 为什么在做客附有__weak
修饰符的变量时必须看注册及autoreleasepool的目标呢?这是因__weak修饰符只具备对象的去世引用,而于访引用对象的过程遭到,该对象有或为丢弃。如果拿要拜的目标注册到autoreleasepool中,那么以@autoreleasepool块了前都能够担保该目标是。因此,在利用下__weak修饰符的变量时就必将使下登记到autoreleasepool中的目标。

末段一个可非显示的施用 __autoreleasing修饰符的例子,同前讲述的 id
obj 和 id __strong obj 完全一致啊。那么id的指针 id * obj
又何以也?可以由 id __strong obj 的事例类生产 id __strong *obj 吗?
其实,推出去的凡 id __autoreleasing *obj。同样的,对象的指针 NSObject
**obj 便成为了 NSObject *__autoreleasing * obj;

诸如这样,id的指针或对象的指针在无亮指定时会给增大上__autoreleasing修饰符。

遵循,为了博取详细的错误信息,经常会面当措施的参数中传送NSError对象的指针,而不是函数返回值。Cocoa框架中,大多数措施呢祭这种措施,如NSString

stringWithContentsOfFile:encoding:error类方法等。使用该方式的源代码如下所示:

NSError *error = nil;

BOOL result = [obj performOperationWithError:&error];

该方法的声明为:

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

同前面讲述的一样,id的指针或对象的指针会默认附加上__autoreleasing 修饰符,所以等同于以下源代码

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

 参数中颇具NSError对象指针的不二法门,虽然为响应其实施结果,需要生成NSError类对象,但也必须符合内存管理的思量方式。

当alloc/new/copy/mutableCopy方法返回值取得的目标是温馨变并装有的,其它情况下就算是得不友好变并有的目标。因此,使用下__autoreleasing修饰符的变量作为目标获得参数,与除alloc/new/copy/mutableCopy外其余方式的返回值取得对象了相同,都见面登记到autoreleasepool,并获取自己别并有所的靶子。

遵performOperationWithError 方法的源代码就相应是下面这样

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

 // 错误发生
 *error = [[NSError alloc] initWithDomain:MyAppDomain code:errorCode userInfo:nil];
return NO;

}

// 因为声明为NSError *__autoreleasing *类型的error作为*error被赋值,所以能够返回注册到autoreleasepool中的对象

 然而,下面的源代码会时有发生编译器错误:

NSError *error = nil;

NSError **pError = &error;

赋值给对象指针时,所有权修饰符必须一致。

error:initializing 'NSError *__autoreleasing *' with an expression of type 'NSError *_strong *'changes retain/release properties of pointer 

此时,对象指针必须加__strong 修饰符

NSError *error = nil;
NSError *__strong *pError = &error;
// 编译正常

当然,对于其他所有权修饰符也是一样

NSError __weak *error = nil;
NSError * __weak *pError = &error;
// 编译正常

NSError __unsafe_unretained *unsafeError = nil;
NSError * __unsafe_unretained *pUnsafeError = &unsafeError;
// 编译正常

 前面的主意参数中采取了副__autoreleasing 修饰符的靶子指针类型。

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

 然而调用方却用了从__strong 修饰符的目标指针类型;

NSError __strong *error = nil;

BOOL result = [obj  performOperationWithError:&error];

 对象指针型赋值时,其所有权修饰符必须一致,但怎么该源代码没有警示便顺通过编译了也?实际上,编译器自动地拿该源代码转化成了脚的样式。

NSError __strong *error = nil;

NSError __autoreleasing *tmp = error;

BOOL result = [obj performOperationWithError:&tmp];

error = tmp;

 当然也得以显得地指定方法参数中目标指针类型的所有权修饰符

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

 像该源代码的扬言一样,对象非挂号及autoreleasepool也能够传递。但是前为说过,只有当alloc/new/copy/mutableCopy
方法的回值如果博对象时,能够和好别并兼有对象。其他情况就算为“取得不自己别并拥有的靶子”,这些务必牢记。为了在运用参数取得对象时,贯彻内存管理的思量方式,我们只要用参数声明也下__autoreleasing修饰符,但每当展示的指定
__autoreleasing
修饰符时,必须小心对象变量要为机关变量(包括有变量、函数和艺术参数)。

 

下我们转移个话题,详细了解一下@autoreleasepool.如以下源代码所示,ARC无效时,可以将NSAutoreleasePool对象嵌套使用。

// ARC无效
NSAutoreleasePool *pool0 = [[NSAutoreleasePool alloc] init];

NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];

NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];

id obj = [[NSObject alloc] init];

[obj autorelease];

[pool2 drain];

[pool1 drain];

[pool0 drain];

同样的,@autoreleasepool 也能够嵌套使用

@autoreleasepool{
    @autoreleasepool{
         @autoreleasepool{
          id __autoreleasing obj = [[NSObject alloc] init];
    }
  }
}

 比如,在iOS应用程序模板被,像下的main函数一样,@autoreleasepool片包含了通顺序。

int main(int argc, char *argv[]){
  @autoreleasepool{
   return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

 NSRunLoop等落实无ARC有效要不行,均能时刻释放注册到autoreleasepool中之靶子。

此外,如果编译器版本为LLVM3.0之上,即使ARC无效@autoreleasepool块吧会以,如下:

// ARC无效
@autoreleasepool{
    id obj = [[NSObject alloc] init];
    [obj autorelease];
}

 因为autoreleasepool
范围为块级源代码表示,提高了次的可读性,所以下当ARC无效时为推荐用
@autoreleasepool块。

另外,无论ARC是否行得通,调试用底非公开函数_objc_autoreleasePoolPrint()都得使

_objc_autoreleasePoolPrint()

下这无异于函数而有效的辅助我们调试注册到autoreleasepool 上之目标。

1.3.4 规则

– 不可知以 retain/realese/retainCOunt/autorelease

– 不能够使 NSAllocateObject/NSDeallcateObject

– 必须遵内存管理的方命名规则

– 不要显示调用dealloc

– 使用@autoreleasepool代替 NSAutoreleasePool

– 不能够使用区域 (NSZone)

– 对象型变量不可知看做C语言结构体的积极分子

– 显示转化 id  和 void *

以ARC无效时,像一下代码这样将id变量强制转换成 void * 变量并无会见来问题。

// ARC无效
id obj = [[NSObject alloc] init];

void *p = obj;

更近异步,将该void * 变量赋值给 id 变量中,调用其实例方法,运行时也不会有问题。

// ARC无效

id o = p;

[o release];

// 但是在ARC有效时这便会引起编译错误

error : implicit coversion of an OC pointer to 'void *' is disallowed with ARC

 id 型或对象型变量赋值给 void *
或者逆向赋值时犹待展开一定的易。如果仅仅想就的赋值,则可使用
“__bridge 转换”

id obj = [[NSObject alloc] init];

void *p = (__bridge void *)obj;

id o = (__bridge id)p

 像这样,通过 “__bridge转换”, id 和 void * 就能够互相C语言转换。

然而转换为 void 的 __bridge 转换,其安全性以及赋值给
__unsafe_unretained 修饰符相近,
甚至会重复没有。如果管理时未留心赋值对象的主人,就见面以悬垂指针导致程序崩溃。

__bridge 转换中还有另外两种植易,分别是 “__bridge_retained 转换” 和
“__bridge_transfer” 转换。

id obj = [[NSObject alloc] init];

void *p = (__bridge_retained void *)obj

 __bridge_retained 转换可要要更换赋值的变量也持有有所赋值的对象。
。。。。。

1.3.5 属性

当ARC有效时,oc类的性也会见发生变化

@property (strong ,nonatomic) NSString *name;

 当ARC有效时,以下但看成这种特性声明遭动用的特性来用。

属性声明和属性和所有权修饰符的对应关系
属性声明的属性 所有权修饰符
assign __unsafe_unretained修饰符
copy   __strong 修饰符(但是赋值的是被赋值的对象)
retain __strong 修饰符
strong __strong 修饰符
unsafe_unretained __unsafe_unretained 修饰符
week __weak 修饰符

上述各种性能赋值给指定的性质被便相当给赋值给附加各属性对应之所有权修饰符的变量中,只有copy属性不是粗略的赋值,它赋值的是透过NSCopying
结构的 copyWithZone 方法复制赋值源所大成的靶子。

此外,在声明类成员变量时,如果同属性声明遭的特性不平等则会挑起编译错误。比如下面的这种情况,

id obj

于声明 id obj 成员变量时,像下这样,定义其性能也weak

编译器会报错。

这时,成员变量的宣示中需要附加 __weak 修饰符。

id __weak obj;

要么应用 strong 属性替代 weak 属性

@property (nonatomic, strong ) id obj