根據字面意思,你們都很容易知道是「只讀」的意思,意味着只生成了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性能優化
這個實在沒什麼可說的,默認的修飾詞就是readWrite,表明可讀可寫多線程
咱們先看一下官方文檔app
默認的屬性修飾詞,按官方文檔上說即便從不一樣的線程經過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修飾的屬性,並無作鎖的操做,多線程同時進行setter/getter操做,並不能保證獲得一個完整的value,因此相對atomic來講nonatomic修飾的屬性訪問速度更快,並且平時對線程安全咱們更傾向於使用信號量、NSLock和synchronized去控制線程安全,他們都能保證代碼塊的原子性,因此幾乎全部的屬性都用nonatomic去修飾。性能
通常來講,咱們都用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仍是有地址的。
若是把上面的代碼
@property (nonatomic, assign) NSMutableArray * array;換成 @property (nonatomic, weak) NSMutableArray * array;
這個時候程序並不會崩潰,若是你打個斷點的話會發現array被自動置爲nil,而OC的特性使得像nil發送消息並不會崩潰,這就是weak和assgin最大的區別,此外weak必須用於修飾對象,這和他自動置爲nil相關,若是強行使用weak修飾基本數據類型,編譯器會報一個大大的紅色錯誤!
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永遠不會被釋放,這就是所謂的強引用。
在說copy與mutableCopy以前咱們先看看官方文檔對深拷貝與淺拷貝的闡釋,以下
對象拷貝 - 從新申請一片內存保留這個對象,與原對象之間沒有半點關係。
指針拷貝 - 實際上至關於引用計數+1,被拷貝的和拷貝的引用同一個對象。
接下來咱們分兩個方面作測試:
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則不一樣。
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 | 深拷貝 |
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則不一樣。
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返回的是一個可變的對象(有興趣的請自行驗證)。
經過上述對copy與strong的描述,copy和strong最終都會返回一個引用計數爲1的對象,因此單單從內存管理上說copy和strong並沒有任何區別,可是copy始終會返回一個不可變對象,他的值是不會被修改的。而strong不一樣,被strong修飾的對象,可能會被可變對象賦值,從而在外部致使不可預料的被更改的狀況。總而言之,是否使用copy或strong仍是根據具體場景來定,這裏還有個性能優化的小技巧,若是copy的是可變的對象,會對它作一次徹底深拷貝/不徹底深拷貝,性能上是確定不如strong直接引用計數+1來的快。