iOS 修飾詞 詳解

前言:

最近公司在擴(kuò)招赃阀,做為公司僅有的唯一一個(gè)首席iOS開(kāi)發(fā)工程師(手動(dòng)滑稽),我不得不硬著頭皮上陣吟税。

然后卻發(fā)現(xiàn)很多人的水平和年限嚴(yán)重不符凹耙,公司招的人都是3年+以上經(jīng)驗(yàn)的人,然而這些人中有一半連修飾詞的作用也說(shuō)的模棱兩可肠仪,加上自己水平也不高肖抱,對(duì)以后的職業(yè)生涯產(chǎn)生了嚴(yán)重的危機(jī)感,遂決定以后每周希望能寫一篇有價(jià)值的文章异旧,與君共勉意述,今天就說(shuō)說(shuō)iOS常見(jiàn)的幾個(gè)修飾詞。

一吮蛹、readOnly荤崇,readWrite

readOnly:

根據(jù)字面意思,大家都很容易知道是“只讀”的意思潮针,意味著只生成了getter方法术荤,而沒(méi)有生成setter方法,如果這時(shí)候調(diào)用setter方法每篷,會(huì)報(bào)一個(gè)Assignment to readonly property錯(cuò)誤

PS:這里順便說(shuō)一下self.和_的區(qū)別

self.就是調(diào)用property自動(dòng)生成的getter和setter方法瓣戚,而_則是直接去調(diào)用實(shí)例變量(property會(huì)自動(dòng)生成一個(gè)實(shí)例變量,如果你重寫了getter與setter方法焦读,property自動(dòng)生成的實(shí)例變量就無(wú)效了子库,需要手動(dòng)去申明一個(gè)實(shí)例變量或者用@@synthesize).

回到正題,那么這意味著我們就完全沒(méi)辦法去修改readOnly的屬性了嗎矗晃?不然仑嗅,如果你嘗試一下setValue:forKey:,你就會(huì)發(fā)現(xiàn)竟然改變成功了,amazing仓技,讓我們來(lái)看看代碼:

@interface MyFirstClass : NSObject

@property (nonatomic, copy, readonly) NSString * string;

@end


#import "MyFirstClass.h"

@implementation MyFirstClass

- (instancetype) init{

    self = [super init];
    if (self) {
        _string = @"來(lái)改變我啊";
    }

    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 === 來(lái)改變我啊
2018-03-16 11:08:58.932454+0800 PropertyDesc[5681:445705] string === 改變了

而如果這個(gè)時(shí)候在MyFirstClass里加入

@implementation MyFirstClass

- (instancetype) init{
    self = [super init];
    if (self) {
        _string = @"來(lái)改變我啊";
    }
    
    return self;
}

+ (BOOL) accessInstanceVariablesDirectly{
    return NO;
}

@end
在運(yùn)行鸵贬,boom,系統(tǒng)會(huì)報(bào)以下錯(cuò)誤
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.'

沒(méi)有找到當(dāng)前要賦值的屬性浑彰,那么accessInstanceVariablesDirectly究竟是什么呢恭理,我們打開(kāi)蘋果的官方文檔找到Key-Value Coding Programming Guide

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

在這里可以看到,如果這個(gè)方法設(shè)為YES郭变,訪問(wèn)器就會(huì)去尋找名稱為_(kāi)<key>, _is<Key>, <key>, or is<Key>的成員變量颜价,如果為NO,就會(huì)跳到第6步诉濒,第6步就會(huì)報(bào)[valueForUndefinedKey:]錯(cuò)誤周伦。
總結(jié):
readOnly并不能完全保證只讀,我們可以通過(guò)KVC嘗試去修改其值未荒。
PS:有興趣的小伙伴可以嘗試去修改別人的SDK专挪,包括蘋果爸爸的

readWrite:

這個(gè)實(shí)在沒(méi)什么可說(shuō)的,默認(rèn)的修飾詞就是readWrite片排,代表可讀可寫

二寨腔、nonatomic、atomic

我們先看一下官方文檔


蘋果官網(wǎng)對(duì)兩者的說(shuō)法

atomic:

默認(rèn)的屬性修飾詞率寡,按官方文檔上說(shuō)即使從不同的線程通過(guò)getter或setter方法去訪問(wèn)屬性也能完全的獲取到或設(shè)置值迫卢,從這里也可以看出,atomic并不是線程安全的冶共,因?yàn)閍tomic只能保證通過(guò)setter和getter方法能獲取到一個(gè)完整的value乾蛤,如果A線程在getter,B捅僵、C線程在setter家卖,可能A獲取到的值是BC執(zhí)行之后的值,也可能是BC線程執(zhí)行完之前的值庙楚,也可能是B C線程任何一個(gè)線程執(zhí)行完的值上荡。
因此atomic的偽代碼大概如下:

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

- (NSString *) string{
    @synchronized(self) {
        return _ string;
    }
}

nonatomic:

相對(duì)而言,通過(guò)nonatomic修飾的屬性馒闷,并沒(méi)有做鎖的操作酪捡,多線程同時(shí)進(jìn)行setter/getter操作,并不能保證得到一個(gè)完整的value窜司,所以相對(duì)atomic來(lái)說(shuō)nonatomic修飾的屬性訪問(wèn)速度更快沛善,而且平時(shí)對(duì)線程安全我們更傾向于使用信號(hào)量航揉、NSLock和synchronized去控制線程安全塞祈,他們都能保證代碼塊的原子性,所以幾乎所有的屬性都用nonatomic去修飾帅涂。

三议薪、assign尤蛮、weak與strong

assign:

一般來(lái)說(shuō),我們都用assign去修飾OC的基本數(shù)據(jù)類型斯议,but why产捞?
因?yàn)閍ssign并不會(huì)使對(duì)象的引用計(jì)數(shù)加1,也就是說(shuō)如果用assign去修飾一個(gè)對(duì)象哼御,這個(gè)對(duì)象會(huì)立即被釋放坯临,重要的是assgin在被釋放的時(shí)候是不會(huì)自動(dòng)置為nil,還是保留對(duì)象的指針地址恋昼,會(huì)形成野指針看靠,這個(gè)時(shí)候向其發(fā)送消息就會(huì)崩潰,簡(jiǎn)單實(shí)驗(yàn)的代碼如下:

@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)];
}

在運(yùn)行到最后一步的時(shí)候程序會(huì)崩潰報(bào)EXC_BAD_ACCESS的錯(cuò)誤液肌,如果打斷點(diǎn)的話會(huì)發(fā)現(xiàn)在執(zhí)行到這步的時(shí)候array還是有地址的挟炬。

weak:

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

這個(gè)時(shí)候程序并不會(huì)崩潰,如果你打個(gè)斷點(diǎn)的話會(huì)發(fā)現(xiàn)array被自動(dòng)置為nil嗦哆,而OC的特性使得像nil發(fā)送消息并不會(huì)崩潰谤祖,這就是weak和assgin最大的區(qū)別,此外weak必須用于修飾對(duì)象老速,這和他自動(dòng)置為nil相關(guān)粥喜,如果強(qiáng)行使用weak修飾基本數(shù)據(jù)類型,編譯器會(huì)報(bào)一個(gè)大大的紅色錯(cuò)誤烁峭!

strong:

strong的作用和assign和weak恰恰相反容客,strong也是屬性默認(rèn)的修飾詞,代表著被修飾的對(duì)象引用計(jì)數(shù)+1

如果把上面的代碼
@property (nonatomic, assign) NSMutableArray * array;換成
@property (nonatomic, strong) NSMutableArray * array;

self.secondClass.array = [NSMutableArray array];

最后一句代碼可以解釋為[NSMutableArray array]創(chuàng)造了一個(gè)對(duì)象A约郁,此時(shí)A的引用計(jì)數(shù)為1悼瓮,self.secondClass.array做為對(duì)象B朴艰,把A賦值給B的時(shí)候,A的引用計(jì)數(shù)加1,此時(shí)A的引用計(jì)數(shù)為2菠齿,B指向了A,然后編譯器會(huì)自動(dòng)對(duì)A進(jìn)行釋放操作(因?yàn)槭蔷植孔兞?碱屁,A的引用計(jì)數(shù)-1刁绒。在擁有B的對(duì)象不釋放的時(shí)候,A的引用計(jì)數(shù)永遠(yuǎn)不可能為0坊罢,除非你手動(dòng)釋放或者把B指向一個(gè)新的對(duì)象续担,這樣A永遠(yuǎn)不會(huì)被釋放,這就是所謂的強(qiáng)引用活孩。

四物遇、copy與mutableCopy

蘋果官網(wǎng)對(duì)對(duì)象拷貝的說(shuō)法

在說(shuō)copy與mutableCopy之前我們先看看官方文檔對(duì)深拷貝與淺拷貝的闡釋,如下

深拷貝:

對(duì)象拷貝 - 重新申請(qǐng)一片內(nèi)存保留這個(gè)對(duì)象,與原對(duì)象之間沒(méi)有半點(diǎn)關(guān)系询兴。

淺拷貝:

指針拷貝 - 實(shí)際上相當(dāng)于引用計(jì)數(shù)+1乃沙,被拷貝的和拷貝的引用同一個(gè)對(duì)象。
接下來(lái)我們分兩個(gè)方面做測(cè)試:

1.對(duì)非集合類對(duì)象的copy操作诗舰,以NSString為例

對(duì)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

可以看出對(duì)string和copyString的地址是一樣的警儒,而mutableCopyString則不同。

對(duì)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

可以看出對(duì)string與copyString眶根、mutableCopyString三者的地址都是不同的蜀铲。
即使改變了原string的value,copyString與mutableCopystring也沒(méi)有改變属百,這與下文對(duì)集合類對(duì)象得出的結(jié)論正好相反蝙茶。

結(jié)論:

對(duì) immutableObject進(jìn)行 copy 操作是指針拷貝,mutableCopy 操作時(shí)對(duì)象拷貝诸老。
對(duì) mutable Object進(jìn)行 copy 和 mutableCopy 都是對(duì)象拷貝隆夯。簡(jiǎn)單的表格圖如下:

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

2.對(duì)集合類對(duì)象的copy操作

對(duì)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則不同别伏。

對(duì)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我們可以看到當(dāng)我們修改了string的值后蹄衷,數(shù)組中的值都變了,但是在 [array addObject:@"2"];的時(shí)候兩個(gè)復(fù)制出來(lái)的數(shù)組的對(duì)象并沒(méi)有變化厘肮?
這里我們要提到一個(gè)新概念 不完全深拷貝(也有人說(shuō)是單層深拷貝 )------ 即雖然新開(kāi)辟了內(nèi)存地址愧口,但是存放在內(nèi)存上的值(也就是數(shù)組里的元素仍然指向原數(shù)組元素值,并沒(méi)有另外復(fù)制一份)类茂,所以說(shuō)上文中的array和mutableCopArray類似于有一個(gè)或多個(gè)相交點(diǎn)的相交鏈表耍属。
而且我們也可以看到不管是copy還是mutableCopy都是不完全深拷貝,三者的地址都是不一樣的巩检。

結(jié)論:

對(duì)immutableObject做copy是指針拷貝厚骗,做mutableCopy是不完全深拷貝。
對(duì)mutableObject做copy或mutableCopy都是不完全深拷貝兢哭。
有趣的是领舰,這與上文的結(jié)論有類似之處。簡(jiǎn)單的表格圖如下:

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

并且如果打個(gè)斷點(diǎn)可以發(fā)現(xiàn)對(duì)任何對(duì)象做copy操作返回的是一個(gè)不可變的對(duì)象迟螺,對(duì)任何對(duì)象做mutableCopy返回的是一個(gè)可變的對(duì)象(有興趣的請(qǐng)自行驗(yàn)證)冲秽。

五、是用copy還是strong矩父?

通過(guò)上述對(duì)copy與strong的描述锉桑,copy和strong最終都會(huì)返回一個(gè)引用計(jì)數(shù)為1的對(duì)象,所以單單從內(nèi)存管理上說(shuō)copy和strong并無(wú)任何區(qū)別窍株,但是copy始終會(huì)返回一個(gè)不可變對(duì)象民轴,他的值是不會(huì)被修改的郑诺。
而strong不同,被strong修飾的對(duì)象杉武,可能會(huì)被可變對(duì)象賦值,從而在外部導(dǎo)致不可預(yù)料的被更改的情況辙售。
總而言之轻抱,是否使用copy或strong還是根據(jù)具體場(chǎng)景來(lái)定,這里還有個(gè)性能優(yōu)化的小技巧旦部,如果copy的是可變的對(duì)象祈搜,會(huì)對(duì)它做一次完全深拷貝/不完全深拷貝,性能上是肯定不如strong直接引用計(jì)數(shù)+1來(lái)的快士八。

六容燕、結(jié)尾

這次的修飾詞講解到此就告一段落了,然后想一下這周寫什么樣的文章婚度。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蘸秘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蝗茁,更是在濱河造成了極大的恐慌醋虏,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哮翘,死亡現(xiàn)場(chǎng)離奇詭異颈嚼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)饭寺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門阻课,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人艰匙,你說(shuō)我怎么就攤上這事限煞。” “怎么了员凝?”我有些...
    開(kāi)封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵晰骑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我绊序,道長(zhǎng)硕舆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任骤公,我火速辦了婚禮抚官,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阶捆。我一直安慰自己凌节,他們只是感情好钦听,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著倍奢,像睡著了一般朴上。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卒煞,一...
    開(kāi)封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天痪宰,我揣著相機(jī)與錄音,去河邊找鬼畔裕。 笑死衣撬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扮饶。 我是一名探鬼主播具练,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼甜无!你這毒婦竟也來(lái)了扛点?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岂丘,失蹤者是張志新(化名)和其女友劉穎占键,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體元潘,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畔乙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翩概。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牲距。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钥庇,靈堂內(nèi)的尸體忽然破棺而出牍鞠,到底是詐尸還是另有隱情,我是刑警寧澤评姨,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布难述,位于F島的核電站,受9級(jí)特大地震影響吐句,放射性物質(zhì)發(fā)生泄漏胁后。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一嗦枢、第九天 我趴在偏房一處隱蔽的房頂上張望攀芯。 院中可真熱鬧,春花似錦文虏、人聲如沸侣诺。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)年鸳。三九已至趴久,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搔确,已是汗流浹背彼棍。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妥箕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓更舞,卻偏偏與公主長(zhǎng)得像畦幢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缆蝉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354