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;
查看 iArray
和 p.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;
查看 mArray
和 p.mArray
地址:
(lldb) p mArray
(__NSArrayM *) $0 = 0x0000000100300260 @"1 element"
(lldb) p p.mArray
(__NSArrayI *) $1 = 0x0000000100203a40 @"1 element"
可变对象 NSMutableArray 的拷贝为深拷贝,拷贝后指向不同的内存地址。
虽然此时 p.mArray
和 mArray
代表不同的对象,但是此刻手痒打印出其元素的地址如下:
(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 中 _self
为 nil
。
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;
}