属性关键字-(进阶篇)

上一篇文章基础篇,我们用NSMutableString为例详细讲解了“非容器可变变量”。相信大家都已经基本掌握了重点在不同关键字的定义下,它们有何相同以及不同之处。
但通过上一篇我们只了解了主题的一半内容而已,接下来内容可能有点多,不过知识点和内容与上一篇大同小异。
如果你有仔细学习基础篇,那么进阶篇学起来会Very Easy。

一. 今天我们先用NSMutableArray举例来讲解容器可变变量

@property(nonatomic, copy)  NSMutableArray *arrayCopy;
@property(nonatomic, strong)NSMutableArray *arrayStrong;
@property(nonatomic, weak)  NSMutableArray *arrayWeak;

- (void)viewDidLoad {
    
    [super viewDidLoad];
    NSMutableArray *testArray = [[NSMutableArray alloc] init];
    NSMutableString *str1 = [[NSMutableString alloc]initWithString:@"test1"];
    NSMutableString *str2 = [[NSMutableString alloc]initWithString:@"test2"];
    
    [testArray addObject:str1];
    [testArray addObject:str2];
    
    self.arrayCopy= testArray;
    self.arrayStrong= testArray;
    self.arrayWeak= testArray;
    
    NSLog(@"testArray   输出:%p, %@", testArray,testArray);
    NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
    NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak[0],_arrayWeak[0]);
    NSLog(@"testArray中的数据引用计数%@", [testArray valueForKey:@"retainCount"]);
    // 输出内容
    testArray   输出:0x604000441470, ( test1, test2 )
    arrayCopy   输出:0x60400022a140, ( test1, test2 )
    arrayStrong 输出:0x604000441470, ( test1, test2 )
    arrayWeak   输出:0x604000441470, ( test1, test2 )
    arrayWeak   输出:0x604000441470, test1
    testArray中的数据引用计数( 3, 3 )
}
  • 这里只有arrayCopy的地址发生了变化,说明arrayCopy为深拷贝,它会重新开辟一块内存来创建一块新容器。但新的容器内copy来的两个元素指为浅拷贝,指针的指向还是str1、str2,这样就会使str1、str2的地址的引用计数+1。
  • arrayWeak、arrayStrong都是浅拷贝、不会开辟新的内存,也不会使容器内部的元素引用计数增加。
接下来我们来给测试数组添加一个元素
NSMutableString *str3 = [[NSMutableString alloc]initWithString:@"test3"];
    [testArray addObject:str3];
    NSLog(@"testArray   输出:%p, %@", testArray,testArray);
    NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
    NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);
    NSLog(@"testArray中的数据引用计数%@", [testArray valueForKey:@"retainCount"]);
    // 输出内容
    testArray    输出:0x604000441470, ( test1, test2, test3 )
    arrayCopy    输出:0x60400022a140, ( test1, test2 )
    arrayStrong  输出:0x604000441470, ( test1, test2, test3 )
    arrayWeak    输出:0x604000441470, ( test1, test2, test3 )
    testArray中的数据引用计数(3, 3, 2)
从结果可以看出
  • 只有arrayCopy内的元素没有增加,因为他是新开辟的内存创建的容器,容器内只有原来两个元素并指向两个元素的地址。
  • arrayStrongarrayWeak都是直接指向testArray的内存地址,所以也会随着testArray的改变而改变。
我们最后来修改原数组中的元素,看是否其他数组会随之变化
    [str1 appendFormat:@"abc"];
    
    NSLog(@"testArray   输出:%p, %@", testArray,testArray);
    NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
    NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
    NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);

    // 输出内容
     testArray   输出:0x604000441470, ( test1abc, test2, test3 )
     arrayCopy   输出:0x60400022a140, ( test1abc, test2 )
     arrayStrong 输出:0x604000441470, ( test1abc, test2, test3 )
     arrayWeak   输出:0x604000441470, ( test1abc, test2, test3 )

容器可变变量总结

  • 修改原数组中的元素后,我们看到arrayStrongarrayWeakarrayCopy内部的元素都发生了变化,
  • 这说明"容器可变变量中的元素"在拷贝过程中无论CopyWeakStrong都是浅拷贝。
  • 但"容器可变变量的容器本身"只有Copy是深拷贝,其余WeakStrong都是浅拷贝。
二. 非容器不变变量
@property (nonatomic, copy)   NSString *stringCopy;
@property (nonatomic, strong) NSString *stringStrong;
@property (nonatomic, weak)   NSString *stringWeak;
@property (nonatomic, assign) NSString *stringAssign;

- (void)p_TestCode
{
    NSString *testStr = [[NSString alloc] initWithUTF8String:"testCode123456"];

    self.stringCopy = testStr;
    self.stringWeak = testStr;
    self.stringStrong = testStr;
    self.stringAssign = testStr;

    NSLog(@"testStr     输出:%p,%@", testStr,testStr);
    NSLog(@"stringCopy  输出:%p,%@",_stringCopy,_stringCopy);
    NSLog(@"stringStrong输出:%p,%@",_stringStrong,_stringStrong);
    NSLog(@"stringWeak  输出:%p,%@",_stringWeak,_stringWeak);
    NSLog(@"testStr中的数据引用计数%@", [testStr valueForKey:@"retainCount"]);

    // 输出内容
    testStr      输出:0xa001003046c32808,testCode
    stringCopy   输出:0xa001003046c32808,testCode
    stringStrong 输出:0xa001003046c32808,testCode
    stringWeak   输出:0xa001003046c32808,testCode
}
  • 赋值后stringCopystringStrongstringWeak的指针都指向testStr的地址。
  • 此时我们testStr的引用计数应该为3,copy为浅拷贝,不开辟新的存储空间,但指向的内存地址引用计数+1。
接下来我们给testStr重新赋值,来看下面的代码
testStr = @"abc";

NSLog(@"testStr     输出:%p,%@", testStr,testStr);
NSLog(@"stringCopy  输出:%p,%@",_stringCopy,_stringCopy);
NSLog(@"stringStrong输出:%p,%@",_stringStrong,_stringStrong);
NSLog(@"stringWeak  输出:%p,%@",_stringWeak,_stringWeak);
NSLog(@"testStr     引用计数%@", [testStr valueForKey:@"retainCount"]);

 // 输出内容
 testStr      输出:0x103806110,aaa
 stringCopy   输出:0xa001003046c32808,testCode
 stringStrong 输出:0xa001003046c32808,testCode
 stringWeak   输出:0xa001003046c32808,testCode
  • stringCopy、stringStrong、stringWeak指向的地址和值仍然是testStr重新赋值之前的。
  • 细心的同学可能会发现当我们重新给testStr赋值时,testStr就指向了一块新的存储空间,但是原有内存地址引用计数为3,-1后扔为2,并不能释放掉这块内存。所以就解释了为什么testStr已经不指向原有内存地址了,但并没有影响其他成员的值
我们再来看下面的代码
self.stringCopy=nil;
self.stringStrong=nil;

NSLog(@"testStr     输出:%p,%@", testStr,testStr);
NSLog(@"stringCopy  输出:%p,%@",_stringCopy,_stringCopy);
NSLog(@"stringStrong输出:%p,%@",_stringStrong,_stringStrong);
NSLog(@"stringWeak  输出:%p,%@",_stringWeak,_stringWeak);
NSLog(@"testStr     引用计数%@", [testStr valueForKey:@"retainCount"]);

 // 输出内容
 testStr     输出:0x103806110,aaa
 stringCopy  输出:0x0,(null)
 stringStrong输出:0x0,(null)
 stringWeak  输出:0x0,(null)
  • stringCopystringStrong设为nil后,发现stringWeak随之改为nil。
  • 因为当testStr重新赋值后,原来指向的内存地址引用计数变为3-1=2,当stringCopystringStrong置空后引用计数为2-2=0。所以weak自然也是null
非容器不变变量总结
  • 不难看出NSString(非容器不可变变量) 和NSMutableString(非容器可变变量) 基本相同。
  • 除了copy不可变变量为浅拷贝,可变变量为深拷贝。那为什么copy不可变变量不自己开辟一个独立的内存出来呢?
  • 原因很简单,因为不可变变量的值不会改变所以系统觉得没必要再开辟一个内存出来让stringCopy指向它,直接指向原来的内存地址就可以了。

三. 不可变容器变量举例NSArray

@property(nonatomic, copy)  NSArray *arrayCopy;
@property(nonatomic, strong)NSArray *arrayStrong;
@property(nonatomic, weak)  NSArray *arrayWeak;

NSString *str1 = [[NSMutableString alloc]initWithString:@"test1"];
NSString *str2 = [[NSMutableString alloc]initWithString:@"test2"];

NSArray *testArray = [[NSArray alloc] initWithObjects:str1,str2, nil];

self.arrayCopy= testArray;
self.arrayStrong= testArray;
self.arrayWeak= testArray;

NSLog(@"testArray   输出:%p, %@", testArray,testArray);
NSLog(@"arrayCopy   输出:%p, %@",_arrayCopy,_arrayCopy);
NSLog(@"arrayStrong 输出:%p, %@",_arrayStrong,_arrayStrong);
NSLog(@"arrayWeak   输出:%p, %@",_arrayWeak,_arrayWeak);
NSLog(@"testArray中的数据引用计数%@", [testArray valueForKey:@"retainCount"]);
// 输出内容
testArray   输出:0x60400042a460, ( test1, test2 )
arrayCopy   输出:0x60400042a460, ( test1, test2 )
arrayStrong 输出:0x60400042a460, ( test1, test2 )
arrayWeak   输出:0x60400042a460, ( test1, test2 )
testArray中的数据引用计数( 2, 2 )
  • 在不可变容器变量中,容器本身都是浅拷贝包括copy,同NSString
  • CopyStrongWeakAssign都是浅拷贝都不会增加容器内部元素的引用计数

以下为全篇总结:CopyStrongAssginWeak

可变变量中:
  • Copy会开辟一块新的内存;而StrongAssginWeak不会,他们只是将指针指向保存值的内存对应的地址
  • Strong会再指向地址后对该内存引用计数+1
  • WeakAssgin不会增加引用计数,但会在指向的地址引用计数为0时将值置为空,并且Weak会将内存置为nil,而Assgin不会,Assgin会在内存被重写之前继续输出,一旦内存被重写后会引起程序崩溃
不可变变量中:

变量本身不可修改Copy没有必要开辟一块新的内存存放一摸一样的内容,所以默认为浅拷贝
StrongAssginWeak则和可变变量保持一致

容器:

容器本身遵守上面可变与不可变的原则,只是内部元素只会被浅拷贝


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

推荐阅读更多精彩内容

  • 原子性 原子性就nonatomic和atomic两个关键字 atomic与nonatomic atomic是默认的...
    萨缪阅读 368评论 0 2
  • 本文逻辑图: 在知道他们区别之前,我们首先要知道NSObject对象的赋值操作做了哪些操作。 A=C其实是在内存中...
    壮了个壮阅读 20,795评论 40 148
  • 之前写过 《iOS属性关键字》基础篇,便于初学者理解,但没有从根本上去说明,属性的本质,这次就更进一步说明 属性的...
    wnido阅读 729评论 0 3
  • 307、setValue:forKey和setObject:forKey的区别是什么? 答:1, setObjec...
    AlanGe阅读 1,537评论 0 1
  • 平时的积累记录下,记性差,方便平时查找 一 ARC和MRC的 小入门 1MRC在ARC之前,MRC就是传统的手工内...
    howhyone阅读 373评论 0 0