内存管理语义

MRC

1. assign

MRC 下 assign 为属性的默认修饰符,无论是简单的数据类型,还是指向对象的指针。

@property (nonatomic) NSString *name;

等价于:

@property (nonatomic, assign) NSString *name;

assign 主要用于修饰数据类型,数据类型变量的内存由编译器自动管理。比如 NSInteger、CGFloat 等。

assign 修饰对象属性时,其指向一个对象之后,不改变该对象的引用计数。即只引用已创建的对象,而不持有对象。

assign 修饰的属性不持有对象,当其指向的对象在别处释放后,该指针变为悬挂指针也叫野指针。

2. retain

retain 修饰的属性会持有它所指向的对象,对象的引用计数 +1,当不再需要使用该对象时需调用 release 释放。

@property 修饰的变量会自动合成 getter 和 setter 方法,手动调用 setter 方法时需保留新值再释放旧值。

3. copy

只能用于修饰对象属性,将对象赋值给 copy 属性时,一般情况下属性会持有该对象的一份拷贝。

NSObject 虽然声明了 copy 方法,但没有实现。自定义的对象需实现 NSCopying 协议的 copyWithZone: 方法才能实现拷贝。

copy 分为深拷贝和浅拷贝。对于 Foundation 中含有可变版本的对象类型,对其不可变版本的 copy 为浅拷贝,对于可变对象的 copy 为深拷贝。

  • 不可变对象 NSArray:
@property (nonatomic, copy) NSArray *imArray;

测试示例:

NSArray *iArray = @[@"Jone"];
p.imArray = iArray;

查看 iArrayp.imArray 地址:

(lldb) p iArray
(__NSArrayI *) $0 = 0x0000000100204170 @"1 element"
(lldb) p p.imArray
(__NSArrayI *) $1 = 0x0000000100204170 @"1 element"

可以看出对于 NSArray 对象的拷贝为浅拷贝,它们仍然指向同一个内存地址。

  • 可变对象 NSMutableArray
@property (nonatomic, copy) NSMutableArray *mArray;

测试示例:

NSMutableArray *mArray = [[NSMutableArray alloc] initWithObjects:@"Jone", nil];
p.mArray = mArray;

查看 mArrayp.mArray 地址:

(lldb) p mArray
(__NSArrayM *) $0 = 0x0000000100300260 @"1 element"
(lldb) p p.mArray
(__NSArrayI *) $1 = 0x0000000100203a40 @"1 element"

可变对象 NSMutableArray 的拷贝为深拷贝,拷贝后指向不同的内存地址。
虽然此时 p.mArraymArray 代表不同的对象,但是此刻手痒打印出其元素的地址如下:

(lldb) p mArray[0]
(__NSCFConstantString *) $2 = 0x0000000100002108 @"Jone"
(lldb) p p.mArray[0]
(__NSCFConstantString *) $3 = 0x0000000100002108 @"Jone"

虽然对可变对象的 copy 为深拷贝,但其内容相同的元素,仍然是对同一个对象的引用。

在使用含有可变版本的对象类型时,其不可变版本需用 copy 修饰符,比如: NSString、 NSArray 等,防止外部将可变版本的对象赋值给该对象属性,然后该可变对象又被意外修改,会造成该属性的内容也被修改。

@property (nonatomic, retain) NSArray *imArray; // 不可变对象推荐使用 copy
NSMutableArray *mArray = [[NSMutableArray alloc] initWithObjects:@"Jone", nil];
p.imArray = mArray;
[mArray addObject:@"iOSTalk"]; // 此时 p.imArray 被修饰,需使用 copy 修饰 imArray。
(lldb) po p.imArray
<__NSArrayM 0x1007005c0>(
Jone,
iOSTalk
)

在使用可变对象作为属性时,慎用 copy 修饰符。如果将可变对象赋值给该属性,该属性持有的对象将会是不可变版本的,如果再对其执行可变版本的方法,将会造成 crash。

@property (nonatomic, copy) NSMutableArray *mArray;// 可变对象慎用 copy
NSMutableArray *mArray = [[NSMutableArray alloc] initWithObjects:@"Jone", nil];
p.mArray = mArray;
[p.mArray addObject:@"iOSTalk"]; // Crash

4. unsafe_unretained

语义等同于 assign,一般用于修饰对象,也可用于修饰数据类型。

ARC

当 ARC 有效时,以下属性可用于属性声明:

属性声明的属性 所有权修饰符
assign __unsafe_unretain 修饰符
copy __strong 修饰符(赋值的是被复制的对象)
retain __strong 修饰符
strong __strong 修饰符
unsafe_unretain __unsafe_unretained 修饰符
weak __weak 修饰符

1. assign/copy/retain

同 MRC 下的语义。

2. strong

strong 为 ARC 下属性的默认内存管理语义,语义等同于 retain。变量的所有权修饰符为 __strong

被赋值时会持有对象,阻止对象被释放。

在 YYMemoryCache 中有这么一段代码:

__weak typeof(self) _self = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        __strong typeof(_self) self = _self;
        if (!self) return;
        [self _trimInBackground];
        [self _trimRecursively];
    });

这段代码先是使用 _self弱引用 self 再在 block 中使用 __strong 修饰符强引用 self。使用 _self 是为了防止引用循环。而在 block 中使用强类型的 self 是为了防止 YYMemoryCache 实例被释放后,已经提交至队列执行的 block 中 _selfnil

3. weak

weak 修饰符弱引用对象,不改变对象的引用计数,当其指向的对象被销毁时,它会自动的置为 nil。变量权限修饰符为 __weak,常用于容易造成循环引用的地方。

__weak 实现原理:将 __weak 指向的对象地址作为键值,__weak 指针变量的地址注册到 weak 表。

由于注册、以及将 weak 变量置为 nil 有一定的开销,如果大量的使用 weak 变量,会消耗一定的 CPU 资源。

4. unsafe_unretain

unsafe_unretain 语义等同于 assgin ,ARC 下编译器不负责管理其修饰属性的内存。功能类似于 weak 但唯一不同的区别在于,weak 所指向的对象被销毁后会被赋值为 nil,而 unsafe_unretain 修饰的属性变量成为悬挂指针。

由于 __weak__unsafe_unretain 都不持有对象,如下两行代码都有引起编译器警告。

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

__unsafe_unretain 可在结构体中修饰对象, 而其他修饰符都不可以。

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

推荐阅读更多精彩内容

  • 开发中使用属性很频繁,常见的属性内存管理语义主要有 strong、copy、weak、assign,本次主要介绍下...
    Ching_Han阅读 565评论 0 2
  • assign:'设置方法'只会针对「纯量类型」如CGFloat、NSInteger等 strong:表明「拥有关系...
    Funcy1Day阅读 376评论 0 0
  • 2016.5.15日上午,大雨,天气冷,地点第二教学楼。大学里最特别的一课开始了,像是一场阅兵式,在这里我们被检验...
    相遇1827阅读 325评论 0 0