iOS 内存管理篇(三)---__strong/__weak/__unsafe_unretain/__autoreleasing 认识和使用

之前学习的文章链接

iOS内存管理篇(一)---alloc/reatain/release/dealloc方法实现

iOS内存管理篇(二)---NSAutoreleasePool/@autoreleasepool/autorelease理解与管理

前言:说到内存管理,避免不了的就是循环应用和某个变量释放的实际,虽然在实际开发种,ARC会为我们自动的加上引用技术和减少引用技术,但是并不是万能的,百密一疏,还是会在实际开发过程中内存管理出现问题,今天我们要来看的就是__strong,__weak,这些关键词的作用和使用方法

__strong关键字与retain关似,用了它,引用计数自动+1

有如下 Demo:

id obj = [[NSObject alloc]init];

/*等价于*/

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

那么在内存里面是怎么执行这段代码的呢

/*编译器模拟的代码*/
id obj = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(obj,@selector(init));
objc_release(obj);

从上面的代码我们可以看出,系统两次调用objc_msgSend来创建这个对象并为之开辟内存空间,然后在变量作用于失效的时候使用objc_release来释放对象,虽然在 ARC模式下我们不能使用 release,但是系统在运行时为我们自动添加了 release。

那么我们来看看实际的例子吧

有两个变量,是用 strong 修饰的

@property (nonatomic, strong) NSString *str1;
@property (nonatomic, strong) NSString *str2;

做了如下的操作

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

大家可以猜猜打印结果是什么

打印结果是 self.str2 = A

由于str2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"A", 虽然 self.str = nil 了,但是并没有使得 str2的引用计数减1导致释放,所以打印的结果为 A


__weak修饰符提供的功能如同魔法一般

  • 若附有__weak修饰符的变量所引用的对象被废弃,则将 nil赋值给该变量

  • 使用附有__weak修饰符的变量,即是使用注册到__autoreleasepool 中的对象

有如下代码:

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

我们看看系统是如何为我们处理的

/*编译器模拟的代码*/
id obj1;
objc_initWeak(&obj1,obj);
objc_destroyWeak(&obj1);

通过objc_initWeak函数初始化附有__weak修饰符的变量,在变量作用域结束的时候用objc_destroyWeak释放该变量

我们来下一下的具体的例子

@property (nonatomic, strong) NSString *str1;
@property (nonatomic, weak)   NSString *str2;

执行如下的代码

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

结果是 self.str2 = null 为什么会是这个结果呢 分析一下,由于self.string1与self.str2指向同一地址,且str2没有retain内存地址,而 self.str1=nil释放了内存,所以string1为nil。
所以这是符合第一条规则的,因为str2 指向的地址已经被释放了,那么会将 nil 赋值给 str2

在这个过程中__weak帮我们干了什么呢

(1) 从 weak表中获取废弃对象的地址为键值的记录
  (2) 将包含在记录中的所有附有__weak 修饰变量的地址,赋值为 nil
  (3) 从 weak 表中删除该记录
  (4) 从引用技术表中删除废弃对象的地址为键值的记录

我们来看看 weak 的另外一个特性

id __weak obj1 = obj/*obj 是 strong 的*/
NSLog(%@"obj1 = %@",obj1);

这段代码转换为系统编译器码为

id obj1;
objc_initWeak(&obj1,obj);
id temp = objc_loadWeakRetained(&obj1);
objc_autorelease(temp);
NSLog(%"%@",temp);
objc_destroyWeak(obj1);

我们可以注意到有个objc_autorelease这个函数,所以正说明了第二点:使用附有__weak修饰符的变量,即是使用注册到__autoreleasepool 中的对象,因为附有__weak的变量所使用的对象都被注册到了__autoreleasepool里面,所以在@autoreleasepool块结束之前可以放心使用,所以在实际开发过程中,如果大量使用 weak 来修饰变量的话,是会非常耗费 CPU资源的,所以当只有会可能产生循环引用的地方使用 weak 是合适的。


__unsafe_unretain顾名思义就是不安全的,其功能和 weak 差不多

有如下的代码

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

这时候编译器报出警告,该代码对应的模拟代码为

/*编译器模拟的代码*/
id obj = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(obj,@selector(init));
objc_release(obj);

objc_release函数立即释放了生成并持有的对象,这样对象的悬垂指针被赋值给了变量obj 当中

我们来看如下的代码:

@property (nonatomic, strong)             NSString *str1;
@property (nonatomic,__unsafe_unretain)   NSString *str2;

执行如下的代码

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

这次根本就不用输出,在没有输出前,程序已经崩溃了,其实就是野指针造成的,为何会造成野指针呢?同于用unsafe_unretained声明的指针,由于 self.str1=nil已将内存释放掉了,但是str2并不知道已被释放了,所以是野指针。然后访问野指针的内存就造成crash. 所以尽量少用unsafe_unretained关键字。

__autoreleasing:将对象赋值给附有__autoreleasing修饰的变量等同于 ARC无效时调用对象的 autorelease 方法

有如下代码:

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

对应的编译器码:

/*编译器的模拟代码*/
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(obj,@selector(init));
objc_autorelease(obj);
objc_autoreleasePoolPop(pool);
- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{
    NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];
    NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];
    NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
    *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
}
-(void)test
{
    NSError *error = nil;
    [self generateErrorInVariable:&error];
    NSLog(@"Error = %@", error);
}

__autoreleasing则可以使对像延迟释放。比如你想传一个未初始 化地对像引用到一个方法当中,在此方法中实始化此对像,那么这种情况将是__autoreleasing表演的时候。看个示例:

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

对应的编译器码:

- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{
    NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];
    NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];
    NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
    *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
}
-(void)test
{
    NSError *error = nil;
    [self generateErrorInVariable:&error];
    NSLog(@"Error = %@", error);
}

符合内存管理规则:谁分配谁释放。

小结:不管是__strong、__weak、__unsafe_unretain、__autoreleasing 这是为了让我们更好的理解内存管理,而且在必要的时候使用这些修饰符来管理我们的内存,而在实际开发种,一般很少使用到后两者,前两者在循环引用的时候可以用来解循环引用,我们在今后的学习中还会学习和使用到它们,别忘了哟~

此文借鉴的文章链接
iOS中 property中的属性strong 、weak、copy 、assign 、retain 、unsafe_unretained 与autoreleasing区别和作用详解
书籍《Objective-C高级编程 iOS与OS X多线程和内存管理》

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

推荐阅读更多精彩内容