iOS 内存管理(二)ARC属性修饰符

ARC在编译期间,根据Objective-C对象的存活周期,在适当的位置添加retain和release代码。从概念上讲,ARC与手动引用计数内存管理遵循同样的内存管理规则,但是ARC也无法防止循环强引用。

ARC模式下的修饰符来修饰变量和声明属性:

  • 声明变量的修饰符:__strong, __weak, __unsafe_unretained, __autoreleasing
  • 声明属性的修饰符:strong, weak, unsafe_unretained,assign
  • 对象和CoreFoundation对象直接的转换修饰符号:__bridge,__bridge_retained或CFBridgingRetain, __bridge_transfer或CFBridgingRelease。
  • 对于线程的安全,有nonatomic,这样效率就更高了,但是不是线程的。如果要线程安全,可以使用atomic,这样在访问是就会有线程锁。

这里主讲iOS ARC所以就只讲weak strong assign这三个修饰符,__weak __strong __assign跟前面的一样

__strong 修饰符

先来看一段代码:

NSObject *obj1 = [[NSObject alloc]init];
id obj2 = obj1;

__strong 修饰符是id类型和对象类型默认的所有权修饰符,也就是说上面的变量,实际上附加了所有权修饰符,id和对象类型在没有明确的指定所有权修饰符时,默认为__strong修饰符,上面代码实质上跟下面的代码一致

__strong NSObject *obj1 = [[NSObject alloc]init];
__strong id obj2 = obj1;

通过__strong修饰符,不必再次键入retain或者release,完美地满足了“引用计数内存管理的思考方式”:</p>
自己生成的对象,自己持有
非自己生成的对象,自己也能持有
不再需要自己持有的对象时释放
非自己持有的对象无法释放

事列代码

NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = obj1;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
obj1 = nil;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);

输出为:<p>
obj1:<NSObject: 0x100203ab0>,obj2=<NSObject: 0x100203ab0></p>
obj1:(null),obj2=<NSObject: 0x100203ab0>

__weak修饰符

看起来好像通过 __strong 修饰符编译器就能完美地进行内存管理,大师遗憾的是,仅通过__strong修饰符时不能解决有些重大问题的。
这里提到的重大问题就是引用计数试内存管理中必然会发生的“循环引用”的问题

image

例如前面出现的带有__strong修饰符的成员变量在持有对象时,很容易发生循环引用

@interface Test : NSobject {

    id __strong _obj;
}
- (void)setObj:(id)obj;
@end
@implementation Test
- (void)setObj:(id)obj {
    _obj = obj;
}
@end
以下就为循环引用
int main() {
    id test1 = [[Test alloc]init];/*对象a*/
    //test1持有Test对象a的强引用
    id test2 = [[Test alloc]init];/*对象b*/
    //test2持有Test对象b的强引用
    [test1 setObj:test2];
    /*
    test1对象a的obj成员变量持有test2对象b的强引用
    此时,持有test对象b的强引用的变量为Test对象a的_obj和test2
    */
    [test2 setObj:test1];
    /*
    test2对象b的obj成员变量持有test2对象a的强引用
    此时,持有test对象b的强引用的变量为Test对象b的_obj和test1
    */
    /*
    因为test1变量超出其作用域,强引用失效,所以自动释放对象a
    因为test2变量超出其作用域,强引用失效,所以自动释放对象b
    此时,持有test对象a的强引用的变量为test2对象b的_obj
    此时,持有test对象b的强引用的变量为test1对象a的_obj
    内存泄漏出现了,两个对象超出作用域都没有被释放
    */
    return 0;
}
下面这种情况,虽然只有一个对象,但在该对象持有其自身是也发生了循环引用
int main(){
    id test = [[Test alloc]init];
    [test setObj:test];
    //对自身的强引用,自引用
    return 0;
}
image
__weak id obj = [[NSObject alloc]init];
//实际上这样的代码是会报警告
//此代码将自己生成的并吃藕的对象赋值给附有 __weak修饰符的变量 obj。即变量obj持有对持有对象的弱引用,因此,为了不以自己持有的状态来保存自己生成并持有的对象,生成的对象会立即被释放。编译器对此会给出警告。如果想下面这样,将对象赋值给一个strong修饰符的变量后再赋值给附有weak修饰符的变量,就不会发生警告了。
{
    id __strong obj0 = [[NSObject alloc]init];
    id __weak obj1 = obj0;
}

因为带__weak修饰符的变量不持有对象,所以超出其变量作用域是,对象即呗释放。如果像下面这样将先前可能发生循环引用的类成员变量改成附有__weak修饰符的成员变量的话,该现象则可以避免

image
@interface Test : NSObject 
{
 id __weak _obj;    
}
@end

__weak修饰符还有另一有点。在持有某对象的弱引用时,若该对象被废弃,则此引用将自动失效且处于nil被赋值状态(空弱引用)。即下代码

NSObject *obj1 = [[NSObject alloc]init];
__weak NSObject *obj2 = obj1;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);
obj1 = nil;
NSLog(@"obj1:%@,obj2=%@",obj1,obj2);

输出结果为
obj1:<NSObject: 0x100202e50>,obj2=<NSObject: 0x100202e50>
obj1:(null),obj2=(null)
使用__weak修饰符可避免循环引用,通过检查附有__weak修饰符变量
__weak修饰符智能用于iOS5以上及OS XLion以上版本的应用程序,iOS4以及OS X Snow Leopard 的应用程序中可以使用__unsafe_unretained来代替。

__unsafe_unretained 修饰符

__unsafe_unretained修饰符正如其名unsafe所示,不安全的所有权修饰符。尽管arc模式的内存管理是编译器的工作,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。这一点在使用时一定要注意<p>

id __unsafe_unretained obj = [[NSObject alloc] init];<p>

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

warning:Assigning retained object to unsafe_unretained variable; object will be released after assignment

附有__unsafe_unretained修饰符的变量同附有__weak修饰发的变量一样,因为自己生成并持有的对象不能继续为自己所有,所以生成的对象会立即被释放。到这里,__unsafe_unretained修饰符和__weak修饰符时一样的,下面我们来看一看差异。

__unsafe_unretained id obj1 = nil;
({
        id obj2 = [[NSObject alloc] init];
        obj1 = obj2;
        NSLog(@"obj1:%@,obj2:%@",obj1,obj2);
});
NSLog(@"obj1:%@",obj1);

代码执行结果为

obj1:<NSObject: 0x1003007c0>,obj2:<NSObject: 0x1003007c0>
obj1:<NSObject: 0x1003007c0>

<html>
发生了什么,来看一看
</html>

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

也就是说,最后一行的NSLog只是碰巧正常运行而已。虽然访问了已经被废弃的对象,但是应用程序在个别运行状态才会崩溃。
在使用__unsafe_unretained修饰符时,赋值给附有__strong修饰符的变量时有必要确保被赋值的对象确实存在。
但是,在使用前,让我们在一尺想想为什么需要使用附有__unsafe_unretained修饰符变量。
比如在iOS4以及OS X Snow Leopard的应用程序中,必须使用__unsafe_unretained修饰符来替代__weak修饰符。赋值给附有__unsafe_unretained修饰符变量的对象在通过该变量使用时,如果没有确保其实际存在,那么应用程序就会崩溃。

__autoreleasing修饰符

ARC有效是autorelease会如何呢,不能使用autorelease方法。另外,也不能使用NSAutoreleasePool类。这样一来,虽然autorelease无法直接使用,但实际上,ARC有效时autorelease功能起作用的

//ARC无效
{
    NSAutoreleasePool *pool = [[NSAutorelease alloc]init];
    id obj = [[NSObject alloc]init];
    [obj autorelease];
    [pool autorelease];
}
//ARC有效
{
    @autoreleasepool {
        id __autoreleasing obj = [[NSObject alloc]init];
    }
}

指定“@autoreleasepool块”来替代“NSAutoreleasePool类对象生成、持有以及废弃”这一范围。
另外,ARC有效是,要通过将对象赋值给附加了__autoreleasing修饰符的变量来替代调用autorelease方法。对象赋值给附有__autoreleasing对象修饰符的变量等价于ARC五小时调用对象那个的autorelease方法,即对象被注册到autoreleasepool。
也就是说可以理解为,在ARC有效时,用@autoreleasepool块来替代NSAreleasePool类,用附有__autoreleasing修饰符的变量替代autorelease方法。

image

assign属性修饰符跟__unsafe_unretained一致,用来修饰基础数据类型

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容