谈谈你对属性的掌握程度,比如 _ 与 self. 、copy与strong、“=”赋值与setArray的区别

本篇文章的完善版本:对属性变量赋新值时可能引发的血案

今天review同事的代码,发现一个bug,如下代码:
_dataArr = newArr;
其中_dataArr是属性变量,newArr是局部变量,于是问了如下的几个问题:
1.掉用属性使用 __ 与 self. 的区别(不是会调用get方法这么简单)。
2.修饰数据对象属性时使用copy与strong的区别。(不是深拷贝与浅拷贝这么简单)
3.给属性(比如可变数组)赋值时采用“=”赋值与setArray的区别。

提出后发现对该部分基础掌握不是很熟悉,所以决定分享下对于初级开发时常常疑惑而出错的这些地方,于是写了如下这个测试demo分别演示以上问题所导致的结果,注意观察局部变量的变化给属性变量的数据与地址在各种情况下的影响。


#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) NSMutableArray *mArrStrong;
@property (nonatomic, copy)   NSMutableArray *mArrCopy;

@property (nonatomic, strong) NSMutableArray *mArrStrong2;
@property (nonatomic, copy)   NSMutableArray *mArrCopy2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //_mArrStrong = [NSMutableArray array];
    //_mArrCopy = [NSMutableArray array];
    _mArrStrong2 = [NSMutableArray array];
    _mArrCopy2 = [NSMutableArray array];
    [self test1];
    [self test2];
}

- (void)test1
{
    NSLog(@"%s", __func__);
    
    NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"1", nil];
    _mArrStrong = arr1;
    _mArrCopy = arr1;
    [arr1 addObject:@"2"];
    NSLog(@"%p", arr1);
    NSLog(@"a:%p %@", _mArrStrong, _mArrStrong);
    NSLog(@"b:%p %@", _mArrCopy, _mArrCopy);
    arr1 = (NSMutableArray *)@[@"000"];
    NSLog(@"额外测试:arr1:%p _mArrStrong:%p _mArrCopy:%p %@ \n%@", arr1, _mArrStrong, _mArrCopy, _mArrStrong, _mArrCopy);
    
    NSMutableArray *arr2 = [NSMutableArray arrayWithObjects:@"1", nil];
    self.mArrStrong = arr2;
    self.mArrCopy = arr2;
    [arr2 addObject:@"2"];
    NSLog(@"%p", arr2);
    NSLog(@"c:%p %@", self.mArrStrong, self.mArrStrong);
    NSLog(@"d:%p %@", self.mArrCopy, self.mArrCopy);
    [arr2 setArray:@[@"000"]];
    NSLog(@"额外测试2:arr2:%p self.mArrStrong:%p self.mArrCopy:%p %@ \n%@", arr2, self.mArrStrong, self.mArrCopy, self.mArrStrong, self.mArrCopy);
}

- (void)test2
{
    NSLog(@"%s", __func__);

    NSMutableArray *arr1 = [NSMutableArray arrayWithObjects:@"1", nil];
    [_mArrStrong2 setArray:arr1];
    [_mArrCopy2 setArray:arr1];
    [arr1 addObject:@"2"];
    NSLog(@"%p", arr1);
    NSLog(@"e:%p %@", _mArrStrong2, _mArrStrong2);
    NSLog(@"f:%p %@", _mArrCopy2, _mArrCopy2);
    
    NSMutableArray *arr2 = [NSMutableArray arrayWithObjects:@"1", nil];
    [self.mArrStrong2 setArray:arr2];
    [self.mArrCopy2 setArray:arr2];
    [arr2 addObject:@"2"];
    NSLog(@"%p", arr2);
    NSLog(@"g:%p %@", self.mArrStrong2, self.mArrStrong2);
    NSLog(@"h:%p %@", self.mArrCopy2, self.mArrCopy2);
}

@end

输出如下:


2016-10-13 19:29:29.018 testArr[40794:1137604] -[ViewController test1]
2016-10-13 19:29:29.018 testArr[40794:1137604] 0x7f89cac0f3b0
2016-10-13 19:29:29.018 testArr[40794:1137604] a:0x7f89cac0f3b0 (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] b:0x7f89cac0f3b0 (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] 额外测试:arr1:0x7f89cd000f00 _mArrStrong:0x7f89cac0f3b0 _mArrCopy:0x7f89cac0f3b0 (1, 2) (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] 0x7f89cd00c5d0
2016-10-13 19:29:29.019 testArr[40794:1137604] c:0x7f89cd00c5d0 (1, 2)
2016-10-13 19:29:29.019 testArr[40794:1137604] d:0x7f89cd000f20 (1)
2016-10-13 19:29:29.019 testArr[40794:1137604] 额外测试2:arr2: 0x7f89cd00c5d0 self.mArrStrong: 0x7f89cd00c5d0 self.mArrCopy: 0x7f89cd000f20 (000) (1)
2016-10-13 19:29:29.019 testArr[40794:1137604] -[ViewController test2]
2016-10-13 19:29:29.019 testArr[40794:1137604] 0x7f89cd014250
2016-10-13 19:29:29.019 testArr[40794:1137604] e:0x7f89cae19c10 (1)
2016-10-13 19:29:29.019 testArr[40794:1137604] f:0x7f89cae19b40 (1)
2016-10-13 19:29:29.020 testArr[40794:1137604] 0x7f89cac16e40
2016-10-13 19:29:29.020 testArr[40794:1137604] g:0x7f89cae19c10 (1)
2016-10-13 19:29:29.034 testArr[40794:1137604] h:0x7f89cae19b40 (1)

这样的输出如果你不觉得意外,那么不用继续浏览下文了。下面仔细谈下我的理解。

代码解析:

  • 输出的a与b:

是通过 _ 获取的属性变量,他等同于一个全局变量,所以对于他的修饰copy或strong都不管作用,然后是通过指针赋值引用arr1的地址,从而_mArrStrong和_mArrCopy与arr1指向同一个内存地址,拥有一样的值。所以_mArrStrong和_mArrCopy会由于arr1的变化而变化。

  • 输出的额外测试1:

arr1指向了一个新的内存地址,数据是@[@"000"],而他之前指向的内存还有_mArrStrong和_mArrCopy引用故不会被释放,所以arr1改变了,但是不会影响_mArrStrong与_mArrCopy的地址和数据还是保持之前的。

  • 输出的c与d:

是通过self. 获取的属性变量,他会在返回 _ 变量前掉用get方法,从而与修饰的copy或strong构成了关联。若是strong修饰的,则是浅拷贝,地址与值都保持一致,只是多了一个引用;反之若是copy修饰的,则是深拷贝,获得了一份新的值,但是数据一样。所以arr2的变化会引起self.mArrStrong的变化,而不会改变self.mArrCopy。

  • 输出的额外测试2:

arr2指向的地址的值改变了,引起指向同一地址的self.mArrStrong的值也改变了,与self.mArrCopy无关。

  • 输出的e与f:

是通过setArray方法赋值,_mArrStrong2与_mArrCopy2的地址不会变化,还是指向初始化时指向的地址,但是拥有了新的值为arr1,之后arr1指向的地址的值发生变化也不会联系到_mArrStrong2与_mArrCopy2。

  • 输出的g与h:

虽然self.mArrStrong2是浅拷贝,但是通过setArray赋了新值,地址还是初始化的地址,因此不会随着arr2的变化而变化,self.mArrCopy2同理。

  • 特别提醒:

输出的d,实现的是深拷贝功能,我并没有给self.mArrCopy申请新的内存,但是系统会自动给它申请。那么是何时申请的呢?就在调用self.mArrCopy时系统会判断给它new内存,因此我们在初始化时别用 self.mArrCopy = [NSMutableArray array];这样的方式初始化,因为执行self.mArrCopy时 mArrCopy 为nil,系统会自动new内存, 但是 = 后面又开辟了内存给self.mArrCopy引用,从而导致内存浪费。因此在初始化强引用的属性对象时用 _ 引用进行初始化,如:_mArrStrong = [NSMutableArray array];

总结如下:

只有采用self.的方式获取copy修饰的属性时才会是深拷贝,若是用setArray或是初始化方法赋值时则与修饰无关。无论面向可变的还是不可变的数据对象,包括数组、字符串、字典等都具有一样的性质。若有不恰当之处请指出。

因此我的惯用写法是使用copy修饰数据对象,更新数据时用setArray或是初始化方法。这样做更保险,通过自己管理自己的生命周期,不依赖其它变量。

本篇文章的完善版本:对属性变量赋新值时可能引发的血案

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

推荐阅读更多精彩内容