[iOS] 内存管理相关修饰符

今天跑题跑回来看看内存相关的修饰符吧(strong, weak, assign, unsafe_unretained, retain)

首先先简要说下内存管理哈,简而言之就是iOS现在的ARC机制会给每个对象计算引用数,当引用数为0以后就会被dealloc啦;如果应该被销毁的对象由于引用不为0而无法被销毁,就是我们常见的内存泄漏。

① Strong

strong是我们日常最常见的,也是默认会给属性加上的,他和MRC下的retain其实作用一样,都是将赋值对象的引用数+1,将旧值引用数-1。

-(void) setName: (NSString *) name {
      if (_name != name) {
        [_name release];             //旧值的引用计数-1
        _name = [name retain];           //新值引用计数+1
     }
}

strong和retain都只能修饰OC对象,如果修饰NSIntegar这种基本数据类型会报编译错误哦~

strong不能修饰非OC对象

(但是MRC下用strong和retain修饰block是有区别的,具体可见参考文献。)

我们来尝试一下用strong来修饰Product的name属性~

@interface Product : NSObject
@property (nonatomic, strong) NSString *name;

- (void)printNameAddress;

@end

====================
NSString *sundayName = @"sunday";
Product *sunday = [[Product alloc] init];
sunday.name = sundayName;
NSLog(@"sundayName的对象地址%p, sunday.name的对象地址%p", sundayName, sunday.name);
NSLog(@"sundayName的对象指针地址%p", &sundayName);
[sunday printNameAddress]; //调用product自己去print &name
    
sundayName = nil;
NSLog(@"sundayName的对象指针地址%p", &sundayName);
[sunday printNameAddress];
NSLog(@"sundayName的对象地址%p, sunday.name的对象地址%p", sundayName, sunday.name);

输出:
sundayName的对象地址0x10d8233d8
sunday.name的对象地址0x10d8233d8 //指向相同

sundayName的对象指针地址0x7ffee23de8f0
sunday.name的对象指针地址0x60000148f9f8

//改为nil以后
sundayName的对象指针地址0x7ffee23de8f0
sunday.name的对象指针地址0x60000148f9f8 //地址未变

sundayName的对象地址0x0
sunday.name的对象地址0x10d8233d8 //指向不同了

当我们把sundayName赋值给sunday(prodcut).name的时候,是把@"sunday"的指针给了sunday(prodcut).name,所以@"sunday"的引用计数为2;当持有局部变量sundayName和sunday(prodcut)的代码块结束了,sundayName和sunday(prodcut)的引用计数就变成0了,就会被回收,@"sunday"的retain count也就变为0,也会被回收。

如果将sundayName设为nil,由于代码还没运行结束,sundayName还没有被销毁,只是不再指向@"sunday",但sunday(prodcut).name仍旧指向@"sunday",它的引用计数则变为1。


引用示意图

但strong不是万能的,错用有可能会造成循环引用,这个就是为什么delegate经常需要用weak,以及block块里面经常用weakSelf。

② Weak

weak是将属性指向赋值,但是赋值的retainCount不增加,当赋值的retainCount变为0以后即使有weak的属性指向它,它也会被销毁,weak属性会被置为nil。

据说是底层会自动维护一个weak属性map,当我们用weak修饰属性的时候,map内就会增加一个属性和值的键值对,当值被销毁以后,会遍历这个map,将对应的weak属性置为nil。

@property (nonatomic, weak) Product *smileProduct;

Product *smile1 = [[Product alloc] init];
self.smileProduct = smile1;
smile1 = nil;
NSLog(@"smileProduct:%@", _smileProduct);

输出:
smileProduct:(null)
====================

改为:
@property (nonatomic, strong) Product *smileProduct;
输出:
smileProduct:<Product: 0x600003ce7f20>

对比weak和strong的输出可以看出weak并不影响对象的引用计数,当对象引用归零销毁后,weak属性会为nil;但strong属性是会让对象引用加1的,所以只要有strong属性仍旧指向对象,该对象的retain count就不为0,也就不会被销毁。

一般IBOutlet、block、delegate里面经常会用weak,以及你不希望这个属性会影响对象销毁的时候,IBOutlet是因为通常我们将view拖入到.m文件里面的时候,其实这个view已经在nib里持有了,没有必要再strong持有一次了;delegate和block内之所以用weak其实大概率都是为了避免循环引用。


循环引用

这里以block为例说一下循环引用,这个是我最开始觉得很难理解的一个事儿,主要是智商不够用QAQ


block循环引用

当我们使用block块的时候,如果他不执行完就不会被销毁,例如循环执行的block就一直会存在,并被self持有,那么block的retain count就为1,不会被销毁;如果block以strong的形式持有self,那么只要block不销毁,self的retain count就不会为0也就不会被销毁,于是就形成了循环引用。

只要将block对self的持有变成weak,那么self的retain count就为0,当self销毁的时候,block由于持有者被销毁,它的retain count也就为0了,于是两者都可以被释放。

delegate之所以需要是weak其实也是这样,如果self持有view,那么view肯定不会被释放,如果view.delegate以strong的形式持有self,self就不能释放了。


delegate循环引用

③ Assign

assign和unsafe_unretained真的是一模一样。他们的作用和weak相似,就是指向但是引用数不变,但区别是如果属性指向的对象被释放,指针不会被置为nil,会出现野指针crash。

注意这里说的是对象哦,如果assign指向的是基本数据类型如NSIntegar,那么将有栈来管理,不会出现野指针

将上面的例子改为assign来尝试一下:

@property (nonatomic, assign) Product *smileProduct;

Product *smile1 = [[Product alloc] init];
self.smileProduct = smile1;
smile1 = nil;
NSLog(@"smileProduct:%@", _smileProduct);

代码运行到最后一行会crash,因为当smile1置为nil的时候,它所指向的product对象其实已经可以释放了,并没有强引用指向了,但是_smileProduct的指针没有被置空,当我们想要访问它的时候就会有野指针crash了。

Q: 那么赋值时对象的retain是否是在set方法里面做的呢?

- (void)setSmileProduct:(Product *)smileProduct {
    _smileProduct = smileProduct;
}

//会有warning
//Assigning retained object to unsafe_unretained variable; 
//object will be released after assignment
- (void)setSmileProduct:(Product *)smileProduct {
    _smileProduct = [smileProduct copy];
}

覆写了一下set方法,分别是直接赋值或者copy一下,然而再次运行发现还是crash了,所以对赋值对象的retain之类的其实是在set方法之外做的,无论你如何覆写都是不行的。

所以assign一般用来修饰基本的数据类型,包括基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char, 等等),iOS中默认对基本类型的修饰符就是assign所以不用特殊加的哈

④ Retain

和strong是一样滴,跳过啦

⑤ Copy

上篇介绍了copy顺带探讨了一下权限和读写安全的修饰符,详见https://www.jianshu.com/p/1313aac306b1


大概是起床太早了还没彻底清醒,真是应了老郭的“心中不得宁静,清晨早做文章”了,最近都比较水-。- 下一篇可能是动画相关或者notification吧~ 希望自己变得越来越好,虽然也没什么意义,但有些事情,哪怕结果没有很好,尝试了就不后悔啦,七八年后的时候想起来可能会觉得傻但这就是青春吧,就和现在回忆以前一样,虽然后悔,但这就是已经走过的没有办法回头的岁月,只能keep going~ happy weekend~

Reference:

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,105评论 1 32
  • Cocoa内存管理机制 (1)当你使用new、alloc、copy方法创建一个对象时,该对象的保留计数器值为1.当...
    John_LS阅读 2,775评论 0 6
  • iOS开发中, 之前一直使用swift, 因此对于Objective-C的内存管理机制长期处于混乱的一知半解状态....
    icetime17阅读 845评论 1 8
  • 属性修饰符简述 ios5之前是MRC,内存需要程序员进行管理,ios5之后是ARC,除非特殊情况,比如C框架或者循...
    咖啡绿茶1991阅读 738评论 0 2
  • 第二天早上八点,本与舞娘一行人约好出发前行,我来到她们的住处,径直走进房间,却发现她们还都睡在铺垫上,我愣住了。舞...
    四脚怪兽阅读 131评论 0 0