内存管理语义

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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

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