屬性修飾詞

1、readOnly,readWrite

readOnly:

根據字面意思,你們都很容易知道是「只讀」的意思,意味着只生成了getter方法,而沒有生成setter方法,若是這時候調用setter方法,會報一個Assignment to readonly property錯誤css

PS:這裏順便說一下self.和_的區別html

self.就是調用property自動生成的getter和setter方法,而_則是直接去調用實例變量(property會自動生成一個實例變量,若是你重寫了getter與setter方法,property自動生成的實例變量就無效了,須要手動去申明一個實例變量或者用@@synthesize).數組

回到正題,那麼這意味着咱們就徹底沒辦法去修改readOnly的屬性了嗎?否則,若是你嘗試一下setValue:forKey:,你就會發現居然改變成功了,amazing,讓咱們來看看代碼:安全

@interface MyFirstClass : NSObject @property (nonatomic, copy, readonly) NSString * string; @end #import "MyFirstClass.h" @implementation MyFirstClass - (instancetype) init{ self = [super init]; if (self) { _string = @"來改變我啊"; } return self; } @end - (void)viewDidLoad { [super viewDidLoad]; MyFirstClass * class = [MyFirstClass new]; NSLog(@"string === %@", class.string); [class setValue:@"改變了" forKey:NSStringFromSelector(@selector(string))]; NSLog(@"string === %@", class.string); } Log以下: 2018-03-16 11:08:58.932303+0800 PropertyDesc[5681:445705] string === 來改變我啊 2018-03-16 11:08:58.932454+0800 PropertyDesc[5681:445705] string === 改變了 

而若是這個時候在MyFirstClass里加入ruby

@implementation MyFirstClass

- (instancetype) init{
    self = [super init]; if (self) { _string = @"來改變我啊"; } return self; } + (BOOL) accessInstanceVariablesDirectly{ return NO; } @end 在運行,boom,系統會報如下錯誤 2018-03-16 11:19:21.619747+0800 PropertyDesc[6016:478446] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MyFirstClass 0x6040000088f0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key string.' 

沒有找到當前要賦值的屬性,那麼accessInstanceVariablesDirectly到底是什麼呢,咱們打開蘋果的官方文檔找到Key-Value Coding Programming Guide性能優化

 
963476FC-4E02-4196-BA64-302E16F5E8AD.png

在這裏能夠看到,若是這個方法設爲YES,訪問器就會去尋找名稱爲_<key>, _is<Key>, <key>, or is<Key>的成員變量,若是爲NO,就會跳到第6步,第6步就會報[valueForUndefinedKey:]錯誤。
總結:
readOnly並不能徹底保證只讀,咱們能夠經過KVC嘗試去修改其值。
PS:有興趣的小夥伴能夠嘗試去修改別人的SDK,包括蘋果爸爸的

 

readWrite:

這個實在沒什麼可說的,默認的修飾詞就是readWrite,表明可讀可寫多線程

2、nonatomic、atomic

咱們先看一下官方文檔app


 
蘋果官網對二者的說法

atomic:

默認的屬性修飾詞,按官方文檔上說即便從不一樣的線程經過getter或setter方法去訪問屬性也能徹底的獲取到或設置值,從這裏也能夠看出,atomic並非線程安全的,由於atomic只能保證經過setter和getter方法能獲取到一個完整的value,若是A線程在getter,B、C線程在setter,可能A獲取到的值是BC執行以後的值,也多是BC線程執行完以前的值,也多是B C線程任何一個線程執行完的值。
所以atomic的僞代碼大概以下:ide

- (void)setString:(NSString *)string{ @synchronized(self) { if (_string != string) { [_string release];//MRC _string = [string copy]; } } } - (NSString *) string{ @synchronized(self) { return _ string; } } 

nonatomic:

相對而言,經過nonatomic修飾的屬性,並無作鎖的操做,多線程同時進行setter/getter操做,並不能保證獲得一個完整的value,因此相對atomic來講nonatomic修飾的屬性訪問速度更快,並且平時對線程安全咱們更傾向於使用信號量、NSLock和synchronized去控制線程安全,他們都能保證代碼塊的原子性,因此幾乎全部的屬性都用nonatomic去修飾。性能

3、assign、weak與strong

assign:

通常來講,咱們都用assign去修飾OC的基本數據類型,but why?
由於assign並不會使對象的引用計數加1,也就是說若是用assign去修飾一個對象,這個對象會當即被釋放,重要的是assgin在被釋放的時候是不會自動置爲nil,仍是保留對象的指針地址,會造成野指針,這個時候向其發送消息就會崩潰,簡單實驗的代碼以下:

@interface MySecondClass : NSObject @property (nonatomic, assign) NSMutableArray * array; @end - (void) testMethodTwo{ MySecondClass * class = [[MySecondClass alloc] init]; self.secondClass = class; self.secondClass.array = [NSMutableArray array]; [self.secondClass.array addObject:@(0)]; } 

在運行到最後一步的時候程序會崩潰報EXC_BAD_ACCESS的錯誤,若是打斷點的話會發如今執行到這步的時候array仍是有地址的。

weak:

若是把上面的代碼
@property (nonatomic, assign) NSMutableArray * array;換成 @property (nonatomic, weak) NSMutableArray * array; 

這個時候程序並不會崩潰,若是你打個斷點的話會發現array被自動置爲nil,而OC的特性使得像nil發送消息並不會崩潰,這就是weak和assgin最大的區別,此外weak必須用於修飾對象,這和他自動置爲nil相關,若是強行使用weak修飾基本數據類型,編譯器會報一個大大的紅色錯誤!

strong:

strong的做用和assign和weak偏偏相反,strong也是屬性默認的修飾詞,表明着被修飾的對象引用計數+1

若是把上面的代碼
@property (nonatomic, assign) NSMutableArray * array;換成 @property (nonatomic, strong) NSMutableArray * array; self.secondClass.array = [NSMutableArray array]; 

最後一句代碼能夠解釋爲[NSMutableArray array]創造了一個對象A,此時A的引用計數爲1,self.secondClass.array作爲對象B,把A賦值給B的時候,A的引用計數加1,此時A的引用計數爲2,B指向了A,而後編譯器會自動對A進行釋放操做(由於是局部變量),A的引用計數-1。在擁有B的對象不釋放的時候,A的引用計數永遠不可能爲0,除非你手動釋放或者把B指向一個新的對象,這樣A永遠不會被釋放,這就是所謂的強引用。

4、copy與mutableCopy

 
蘋果官網對對象拷貝的說法

在說copy與mutableCopy以前咱們先看看官方文檔對深拷貝與淺拷貝的闡釋,以下

深拷貝:

對象拷貝 - 從新申請一片內存保留這個對象,與原對象之間沒有半點關係。

淺拷貝:

指針拷貝 - 實際上至關於引用計數+1,被拷貝的和拷貝的引用同一個對象。
接下來咱們分兩個方面作測試:

1.對非集合類對象的copy操做,以NSString爲例

對immutableObject作copy操做
NSString * string = [NSString stringWithFormat:@"1"]; NSString * copyString = [string copy]; NSString * mutableCopyString = [string mutableCopy]; NSLog(@"string:%p", string); NSLog(@"copyString:%p", copyString); NSLog(@"mutableCopyString:%p", mutableCopyString); 

Log以下:

2018-03-19 15:51:38.785253+0800 PropertyDesc[10283:759804] string:0xa000000000000311 2018-03-19 15:51:38.785435+0800 PropertyDesc[10283:759804] copyString:0xa000000000000311 2018-03-19 15:51:38.785518+0800 PropertyDesc[10283:759804] mutableCopyString:0x608000055150 

能夠看出對string和copyString的地址是同樣的,而mutableCopyString則不一樣。

對mutableObject作copy操做
NSMutableString * string = [NSMutableString stringWithFormat:@"1"]; NSString * copyString = [string copy]; NSString * mutableCopyString = [string mutableCopy]; NSLog(@"string:%p - %@", string, string); NSLog(@"copyString:%p - %@", copyString, copyString); NSLog(@"mutableCopString:%p - %@", mutableCopyString, mutableCopyString); [string appendString:@",2"]; NSLog(@"copyString:%p - %@", copyString, copyString); NSLog(@"mutableCopString:%p - %@", mutableCopyString, mutableCopyString); 

Log以下:

2018-03-19 15:51:38.785670+0800 PropertyDesc[10283:759804] string:0x60400005a940 - 1 2018-03-19 15:51:38.785784+0800 PropertyDesc[10283:759804] copyString:0xa000000000000311 - 1 2018-03-19 15:51:38.785834+0800 PropertyDesc[10283:759804] copyString:0xa000000000000311 - 1 2018-03-19 15:51:38.785891+0800 PropertyDesc[10283:759804] mutableCopyString:0x60400005a910 - 1 2018-03-19 15:51:38.786037+0800 PropertyDesc[10283:759804] mutableCopyString:0x60400005a910 - 1 

能夠看出對string與copyString、mutableCopyString三者的地址都是不一樣的。
即便改變了原string的value,copyString與mutableCopystring也沒有改變,這與下文對集合類對象得出的結論正好相反。

結論:

對 immutableObject進行 copy 操做是指針拷貝,mutableCopy 操做時對象拷貝。
對 mutable Object進行 copy 和 mutableCopy 都是對象拷貝。簡單的表格圖以下:

Object Handle Result
immutableObject copy 指針拷貝
immutableObject mutableCopy 深拷貝
mutableObject copy 深拷貝
mutableObject mutableCopy 深拷貝

2.對集合類對象的copy操做

對immutableObject作copy操做
NSArray * array = [NSArray arrayWithObject:@"1"]; NSArray * copyArry = [array copy]; NSMutableArray * mutableCopyArray = [array mutableCopy]; NSLog(@"array:%p", array); NSLog(@"copyArry:%p", copyArry); NSLog(@"mutableCopyArray:%p", mutableCopyArray); 

Log以下

2018-03-19 15:51:38.786167+0800 PropertyDesc[10283:759804] array:0x6000000094c0 2018-03-19 15:51:38.786278+0800 PropertyDesc[10283:759804] copyArray:0x6000000094c0 2018-03-19 15:51:38.786385+0800 PropertyDesc[10283:759804] mutableCopyArray:0x600000240030 

能夠看出array與copyArray的地址是同樣的,而mutableCopyArray則不一樣。

對mutableObject作copy操做
NSMutableString * string = [NSMutableString stringWithFormat:@"1"]; NSMutableArray * array = [NSMutableArray arrayWithObject:string]; NSArray * copyArry = [array copy]; NSMutableArray * mutableCopyArray = [array mutableCopy]; NSLog(@"array:%p", array); NSLog(@"copyArry:%p", copyArry); NSLog(@"mutableCopyArray:%p", mutableCopyArray); [array addObject:@"2"]; [string appendString:@"1"]; NSLog(@"array:%p - %@", array, array); NSLog(@"copyArry:%p - %@", copyArry, copyArry); NSLog(@"mutableCopArray:%p - %@", mutableCopyArray, mutableCopyArray); 

Log以下

2018-03-26 13:36:38.786499+0800 PropertyDesc[10283:759804] array:0x600000240150 2018-03-26 13:36:38.786600+0800 PropertyDesc[10283:759804] copyArry:0x6000000095f0 2018-03-26 13:36:38.786698+0800 PropertyDesc[10283:759804] mutableCopyArray:0x6000002400f0 2018-03-26 13:36:38.786865+0800 PropertyDesc[10283:759804] array:0x600000240150 - ( 11, 2 ) 2018-03-26 13:36:38.787018+0800 PropertyDesc[10283:759804] copyArry:0x6000000095f0 - ( 11 ) 2018-03-26 13:36:38.787142+0800 PropertyDesc[10283:759804] mutableCopyArray:0x6000002400f0 - ( 11 ) 

What??不論是copy仍是mutableCopy咱們能夠看到當咱們修改了string的值後,數組中的值都變了,可是在 [array addObject:@"2"];的時候兩個複製出來的數組的對象並無變化?
這裏咱們要提到一個新概念 不徹底深拷貝(也有人說是單層深拷貝 )------ 即雖然新開闢了內存地址,可是存放在內存上的值(也就是數組裏的元素仍然指向原數組元素值,並無另外複製一份),因此說上文中的array和mutableCopArray相似於有一個或多個相交點的相交鏈表。
並且咱們也能夠看到不論是copy仍是mutableCopy都是不徹底深拷貝,三者的地址都是不同的。

結論:

對immutableObject作copy是指針拷貝,作mutableCopy是不徹底深拷貝。
對mutableObject作copy或mutableCopy都是不徹底深拷貝。
有趣的是,這與上文的結論有相似之處。簡單的表格圖以下:

Object Handle Result
immutableObject copy 指針拷貝
immutableObject mutableCopy 不徹底深拷貝
mutableObject copy 不徹底深拷貝
mutableObject mutableCopy 不徹底深拷貝

而且若是打個斷點能夠發現對任何對象作copy操做返回的是一個不可變的對象,對任何對象作mutableCopy返回的是一個可變的對象(有興趣的請自行驗證)。

5、是用copy仍是strong?

經過上述對copy與strong的描述,copy和strong最終都會返回一個引用計數爲1的對象,因此單單從內存管理上說copy和strong並沒有任何區別,可是copy始終會返回一個不可變對象,他的值是不會被修改的。而strong不一樣,被strong修飾的對象,可能會被可變對象賦值,從而在外部致使不可預料的被更改的狀況。總而言之,是否使用copy或strong仍是根據具體場景來定,這裏還有個性能優化的小技巧,若是copy的是可變的對象,會對它作一次徹底深拷貝/不徹底深拷貝,性能上是確定不如strong直接引用計數+1來的快。

相關文章
相關標籤/搜索