美文网首页
内存管理语义

内存管理语义

作者: SmallflyBlog | 来源:发表于2016-08-20 21:05 被阅读283次

    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;
    }

    相关文章

      网友评论

          本文标题:内存管理语义

          本文链接:https://www.haomeiwen.com/subject/vhmisttx.html