通过对《Objective-C高级编程》的学习,就来总结一下。
解读
相信很多学习iOS编程的人都会认识ARC(自动引用计数),实际上呢,”引用计数内存管理”的本质部分在ARC中并没有改变。根据名字我们可以看出,ARC就是自动帮我们处理”引用计数”的相关部分而已。
从编译单位上的角度来讲,我们是可以对ARC进行有效或者无效的设置的。例如有一些文件可以使用ARC,有一些文章不可以使用ARC。
编译单元 |
---|
ARC有效 |
ARC无效 |
ARC有效 |
ARC无效 |
从上面的表格我们就可以看出,一个应用程序可以混合ARC有效或者无效的二进制形式
Xcode 4.2以上默认设定为所有ARC有效
内存管理的思考方式
引用计数的思考方式就是思考ARC所引起的变化
- 自己生成的对象,自己所持有
- 非自己生成的对象,自己也能持有。
- 自己持有的对象不需要时释放。
- 非自己持有的对象无法释放。
所有权修饰符
Objectve-C编程中为了处理对象,我们可以将对象定义为id类型或者各种对象类型。
定义中这么写到:所谓的对象类型就是指向NSObject这样的Objectve-C类的指针,例如”NSObject”。id类型用于隐藏对象类型的类名部分,是不是有点像c语言的”void * “。
ARC有效的时候,id类型和对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符。所有权修饰符一共有4种。
- _strong修饰符
- _weak修饰符
- _unsafe_unretained修饰符
- _autoreleaing修饰符
_strong修饰符
现在就来总结一下_strong修饰符的相关东西
_strong其实是id类型和对象类型的默认所有权修饰符。观察一下下面的源码,我们刚才提过,_strong是默认的所有权修饰符,ok,那么在下面的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无效的时,该源代码可记录如下:
{
id _strong 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为强引用,所以自己持有对象。
}
//在这片区域因为超出了其作用域,强引用失效。
//所以自动地释放掉自己的持有的对象
//对象的持有者不存在,所以废弃掉该对象。
在此之外,对象的所有者和对象的生命周期是明确的。那么取得非自己生成并持有的对象时又会怎么样呢?
{
id _strong obj = [NSMutableArray array];
}
在NSMutableArray类的array类方法取得的源代码中取得非自己生成并持有的对象,具体做法如下:
{
//自己生成并持有的对象
id _strong obj = [NSMutableArray array];
//因为变量obj为强引用
//所以自己持有对象
}
//变量obj超出作用域,强引用失效。
//自动释放自己所持有的对象。
_strong变量之间是可以互相赋值的。
看以下代码:
id _strong obj0 = [[NSObject alloc] init];
id _strong obj1 = [[NSObject alloc] init];
id _strong obj2 = [[NSObject alloc] init];
obj0 = obj1;
obj2 = obj0;
obj0 = nil;
obj1 = nil;
obj2 = nil;
接下来我们来看一下生成并持有对象的强引用。
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;
//因为obj为nil,所以对B的强引用失效。
//此时,持有B的强引用位obj0和obj2。
obj0 = nil;
//因为obj0位nil,所以对对象B的强引用失效
//此时持有对象B的强引用为obj2
obj2 = nil;
//因为obj2为nil
//对象B的持有者不存在所以废弃掉对象B
通过上面的例子我们可以看出来,_strong不仅可以在变量作用域中有效的管理对象的持有者,在赋值中也可以正确的管理对象的持有者。
当然,即便Objective-C类成员变量,也可以在方法参数上,使用附有_strong修饰符的变量。
@interface Test : NSObjet
{
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;
}
@end
我们来使用这个类
{
id _strong test = [[Test alloc] init];
[test setObject ;[[NSobject alloc] init]];
}
该例子生成并持有对象的状态记录如下:
{
id _strong test = [[Test alloc] init];
//Test持有test对象的强引用
[test setObject:[[NSobject alloc] init]];
//Test对象的obj_成员,
//持有NSobject对象的强引用。
}
//test超出作用域,强引用失效
//自动释放Test
//Test对象的所有者不存在,因此废弃掉该对象。
//废弃掉Test对象的同时。
//Test对象的obj_成员也被废弃掉。
//NSObject对象的强引用失效
//自动释放NSObject对象
//NSObject对象的所有者不存在,因此废弃掉该对象
ok,就这样无需做额外的工作便可以适用于类成员方法及方法参数中。
正如苹果宣称的那样,通过_strong修饰符,不需要在键入retain或者是release,完美的满足了”引用计数内存管理的思考方式”:
- 自己生成的对象,自己所持有
- 非自己生成的对象,自己也能持有。
- 自己持有的对象不需要时释放。
- 非自己持有的对象无法释放。
引用书中的话进行介绍就是:
前两项”自己生成的对象,自己持有”和非自己生成的对象,自己也能持有”,通过对象_strong修饰符变量的赋值便可以达成。通过废弃_strong修饰符的变量或者对变量赋值,都可以做到”不再需要自己持有的对象时释放”。最后一项”非自己持有的对象无法释放”,由于不必在键入release,所以原本就不会执行。这都都满足”引用计数内存管理的思考方式”。
因为id类型和对象类型的所有权修饰符默认为_strong修饰符,所以不需要写上_strong。使用ARC有效及简单的编程遵循了Objective-C内存管理思考方式。