探索weak贺拣、strong、copy捂蕴、assign譬涡、__weak、__strong的用法

更新:

  • 2018.10.12 修改了copy與strong修飾NSString啥辨、NSArray這些類型的解釋和用法

開發(fā)環(huán)境:

Mac系統(tǒng)版本:macOS Mojave 10.14(18A391)
Xcode:Version 10.0 (10A255)

結(jié)論:

就怕你想看結(jié)論又不想看長篇大論涡匀,就把結(jié)論寫到上面,免得你滑屏幕累得慌
  • strong 表示指向并擁有該對象溉知。其修飾的對象引用計(jì)數(shù)會(huì)增加 1陨瘩。該對象只要引用計(jì)數(shù)不為 0 則不會(huì)被銷毀。當(dāng)然強(qiáng)行將其設(shè)為 nil 可以銷毀它级乍。
  • weak 表示指向但不擁有該對象舌劳。其修飾的對象引用計(jì)數(shù)不會(huì)增加。無需手動(dòng)設(shè)置玫荣,該對象會(huì)自行在內(nèi)存中銷毀甚淡。
  • assign 主要用于修飾基本數(shù)據(jù)類型,如 NSInteger 和 CGFloat 捅厂,這些數(shù)值主要存在于棧上贯卦。
  • weak 一般用來修飾對象,assign 一般用來修飾基本數(shù)據(jù)類型焙贷。
  • copystrong 類似撵割。不同之處是 strong 的復(fù)制是多個(gè)指針指向同一個(gè)地址,而 copy 的復(fù)制每次會(huì)在內(nèi)存中拷貝一份對象辙芍,指針指向不同地址睁枕。
  • __weak__strongweakstrong類似外遇,區(qū)別是__weak注簿、__strong修飾變量,而weak跳仿、strong修飾屬性诡渴,而且__weak__strong基本上都是與block相關(guān)菲语;
  • 如果對象類型有對應(yīng)的可變類型妄辩,例如NSString、NSArray山上、NSDictionary等眼耀,需要結(jié)合場景合理使用,而對應(yīng)的可變類型用strong佩憾,使用copy會(huì)導(dǎo)致崩潰哮伟;
  • delegate使用weak修飾;
  • 各種視圖控件推薦使用weak妄帘,官方就是這樣做的楞黄;

探索:

1. 為什么assign不能用來修飾對象類型?

我先引用故胤道長在《iOS面試之道》這本書中的回答:
assign 修飾的對象被釋放后抡驼,指針的地址依然存在鬼廓,造成野指針,在堆上容易造成崩潰致盟。而棧上的內(nèi)存系統(tǒng)會(huì)自動(dòng)處理碎税,不會(huì)造成野指針。
總之一句話馏锡,使用assign修飾對象容易造成崩潰蚣录。

代碼驗(yàn)證環(huán)節(jié):

/// 定義一個(gè)assign修飾的字符串變量
@property (nonatomic, readwrite, assign) NSString *string_assign;
/// 驗(yàn)證代碼
- (void)influenceForNSStringWithAssign {
    /// 此時(shí)self.string_assign的值為 null
    NSLog(@"賦值前:string_assign = %@", self.string_assign);
    {
        NSMutableString *temp = [NSMutableString stringWithString:@"hello world"];
        self.string_assign = temp;
        /// 此處self.string_assign的值為 hello world
        NSLog(@"賦值后:string_assign = %@", self.string_assign);
        [temp appendString:@" changed"];
        /// 此時(shí)self.string_assign的值為 hello world changed
        NSLog(@"原始值修改后:string_assign = %@", self.string_assign);
    }
    /// 此時(shí)超出temp的作用域,temp被釋放眷篇,self.string_assign的指針地址依然存在萎河,成為野指針,此時(shí)使用self.string_assign就會(huì)造成崩潰
    NSLog(@"超出原始值的作用于后:string_assign = %@\n\n", self.string_assign);
}
/// 控制臺(tái)輸出
2018-10-10 17:58:29.141563+0800 MemoryManagerDemo[36403:5926424] 賦值前:string_assign = (null)
2018-10-10 17:58:29.141712+0800 MemoryManagerDemo[36403:5926424] 賦值后:string_assign = hello world
2018-10-10 17:58:29.141827+0800 MemoryManagerDemo[36403:5926424] 原始值修改后:string_assign = hello world changed
(lldb) /// 此處崩潰
2. 使用copy和strong修飾NSString蕉饼、NSArray這類有對應(yīng)可變類型的對象類型有什么區(qū)別虐杯?

在對對象賦值的時(shí)候,如果是使用copy修飾昧港,那么會(huì)在內(nèi)存中將原對象的值拷貝一份擎椰,原對象與該對象指向的是不同的內(nèi)存區(qū)域,原對象在之后做任何修改都與該對象無關(guān)创肥;

而如果是使用strong修飾达舒,那么該對象與原對象雖然是不同的指針對象值朋,但指向的都是同一片內(nèi)存區(qū)域,如果原對象進(jìn)行了修改巩搏,那么即使這個(gè)對象是不可變的昨登,它的值也會(huì)發(fā)生變化;

如果確定在賦值之后贯底,原值不會(huì)修改丰辣,那么使用strong是比較好的選擇,畢竟禽捆,copy會(huì)消耗系統(tǒng)資源笙什,能省一點(diǎn)是一點(diǎn)吧。但是胚想,如果是要對外開放的琐凭,需要外部使用人員賦值的,建議還是使用copy浊服,因?yàn)槟悴恢浪麄儠?huì)不會(huì)改變统屈,你也控制不了,所以為了安全起見臼闻,而且對外開放提供給別人使用的肯定不會(huì)特別多,那么有限的幾個(gè)copy消耗點(diǎn)資源比起安全來囤采,又有什么大不了的呢述呐?

代碼驗(yàn)證環(huán)節(jié):

/// 先定義兩個(gè)使用不同修飾符的屬性
@property (nonatomic, readwrite, strong) NSString *string_strong;
@property (nonatomic, readwrite, copy) NSString *string_copy;
- (void)influenceForNSStringWithStrong {
    NSLog(@"開始測試strong對NSString的影響");
    NSLog(@"賦值前:string_strong = %@", self.string_strong); /* string_strong = (null) */
    {
        NSMutableString *temp = [NSMutableString stringWithString:@"hello world"];
        self.string_strong = temp;
        NSLog(@"賦值后:string_strong = %@", self.string_strong); /* string_strong = hello world */
        [temp appendString:@" changed"];
        NSLog(@"原始值修改后:string_strong = %@", self.string_strong); /* hello world changed */
    }
    NSLog(@"超出原始值的作用于后:string_strong = %@\n\n", self.string_strong); /* hello world changed */
}

- (void)influenceForNSStringWithCopy {
    NSLog(@"開始測試copy對NSString的影響");
    NSLog(@"賦值前:string_copy = %@", self.string_copy); /* string_copy = (null) */
    {
        NSMutableString *temp = [NSMutableString stringWithString:@"hello world"];
        self.string_copy = temp;
        NSLog(@"賦值后:string_copy = %@", self.string_copy); /* string_copy = hello world */
        [temp appendString:@" changed"];
        NSLog(@"原始值修改后:string_copy = %@", self.string_copy); /* string_copy = hello world */
    }
    NSLog(@"超出原始值的作用于后:string_copy = %@\n\n", self.string_copy); /* string_copy = hello world */
}
/// 控制臺(tái)輸出
2018-10-10 18:15:24.559236+0800 MemoryManagerDemo[36553:5936151] 開始測試strong對NSString的影響
2018-10-10 18:15:24.559368+0800 MemoryManagerDemo[36553:5936151] 賦值前:string_strong = (null)
2018-10-10 18:15:24.559460+0800 MemoryManagerDemo[36553:5936151] 賦值后:string_strong = hello world
2018-10-10 18:15:24.559551+0800 MemoryManagerDemo[36553:5936151] 原始值修改后:string_strong = hello world changed
2018-10-10 18:15:24.559650+0800 MemoryManagerDemo[36553:5936151] 超出原始值的作用于后:string_strong = hello world changed


2018-10-10 18:15:24.559742+0800 MemoryManagerDemo[36553:5936151] 開始測試copy對NSString的影響
2018-10-10 18:15:24.559860+0800 MemoryManagerDemo[36553:5936151] 賦值前:string_copy = (null)
2018-10-10 18:15:24.559957+0800 MemoryManagerDemo[36553:5936151] 賦值后:string_copy = hello world
2018-10-10 18:15:24.560050+0800 MemoryManagerDemo[36553:5936151] 原始值修改后:string_copy = hello world
2018-10-10 18:15:24.560151+0800 MemoryManagerDemo[36553:5936151] 超出原始值的作用于后:string_copy = hello world
3. 為什么說weak修飾的對象會(huì)自行在內(nèi)存中銷毀?

這就沒啥說的了蕉毯,直接代碼驗(yàn)證:

@property (nonatomic, readwrite, weak) NSString *string_weak;
- (void)influenceForNSStringWithWeak {
    NSLog(@"開始測試weak對NSString的影響");
    NSLog(@"賦值前:string_weak = %@", self.string_weak); /* string_weak = (null) */
    {
        NSMutableString *temp = [NSMutableString stringWithString:@"hello world"];
        self.string_weak = temp;
        NSLog(@"賦值后:string_weak = %@", self.string_weak); /* string_weak = hello world */
        [temp appendString:@" changed"];
        NSLog(@"原始值修改后:string_weak = %@", self.string_weak); /* string_weak = hello world changed */
    }
    /* string_weak = (null) 乓搬,此處已經(jīng)超出了temp的作用域了,而temp又沒有其他的引用代虾,就會(huì)被釋放进肯,string_weak自然就為null了*/
    NSLog(@"超出原始值的作用于后:string_weak = %@\n\n", self.string_weak); 
}
/// 控制臺(tái)輸出
2018-10-10 18:22:54.118462+0800 MemoryManagerDemo[36613:5941777] 開始測試weak對NSString的影響
2018-10-10 18:22:54.118845+0800 MemoryManagerDemo[36613:5941777] 賦值前:string_weak = (null)
2018-10-10 18:22:54.119251+0800 MemoryManagerDemo[36613:5941777] 賦值后:string_weak = hello world
2018-10-10 18:22:54.119474+0800 MemoryManagerDemo[36613:5941777] 原始值修改后:string_weak = hello world changed
2018-10-10 18:22:54.119673+0800 MemoryManagerDemo[36613:5941777] 超出原始值的作用于后:string_weak = (null)
4. 為什么不能用copy修飾NSMutableString這種可變對象?
@property (nonatomic, readwrite, strong) NSMutableString *mutableString_strong;
@property (nonatomic, readwrite, copy) NSMutableString *mutableString_copy;
- (void)influenceForNSMutableStringWithCopy {
    NSString *temp = @"hello world";
    self.mutableString_strong = [NSMutableString stringWithString:temp];
    self.mutableString_copy = [NSMutableString stringWithString:temp];
    [self.mutableString_strong appendString:@" changed"];
    [self.mutableString_copy appendString:@" changed"];
}
/// 控制臺(tái)輸出
2018-10-10 20:35:49.432473+0800 MemoryManagerDemo[36990:5966214] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000011124329b __exceptionPreprocess + 331
    1   libobjc.A.dylib                     0x00000001107df735 objc_exception_throw + 48
    2   CoreFoundation                      0x00000001112430f5 +[NSException raise:format:] + 197
    3   CoreFoundation                      0x0000000111189dc9 mutateError + 121
    4   MemoryManagerDemo                   0x000000010febf6f5 -[ViewController influenceForNSMutableStringWithCopy] + 309
    5   MemoryManagerDemo                   0x000000010febf3fb -[ViewController influenceForNSMutableString:] + 59
    6   UIKitCore                           0x0000000114ee47c3 -[UIApplication sendAction:to:from:forEvent:] + 83
    7   UIKitCore                           0x000000011501ce85 -[UIControl sendAction:to:forEvent:] + 67
    8   UIKitCore                           0x000000011501d1a2 -[UIControl _sendActionsForEvents:withEvent:] + 450
    9   UIKitCore                           0x000000011501c0e6 -[UIControl touchesEnded:withEvent:] + 583
    10  UIKitCore                           0x00000001156f7334 -[UIWindow _sendTouchesForEvent:] + 2729
    11  UIKitCore                           0x00000001156f8a30 -[UIWindow sendEvent:] + 4080
    12  UIKitCore                           0x0000000114efee10 -[UIApplication sendEvent:] + 352
    13  UIKitCore                           0x0000000114e370d0 __dispatchPreprocessedEventFromEventQueue + 3024
    14  UIKitCore                           0x0000000114e39cf2 __handleEventQueueInternal + 5948
    15  CoreFoundation                      0x00000001111a6b31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    16  CoreFoundation                      0x00000001111a6464 __CFRunLoopDoSources0 + 436
    17  CoreFoundation                      0x00000001111a0a4f __CFRunLoopRun + 1263
    18  CoreFoundation                      0x00000001111a0221 CFRunLoopRunSpecific + 625
    19  GraphicsServices                    0x00000001198f41dd GSEventRunModal + 62
    20  UIKitCore                           0x0000000114ee3115 UIApplicationMain + 140
    21  MemoryManagerDemo                   0x000000010febfce0 main + 112
    22  libdyld.dylib                       0x0000000112ba9551 start + 1
    23  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

上面的代碼執(zhí)行后棉磨,會(huì)導(dǎo)致崩潰江掩,可以看到上面控制臺(tái)輸出的崩潰記錄,奔潰的原因是Attempt to mutate immutable object with appendString:乘瓤,這句話的意思是嘗試用附件字符串來改變不可變對象:环形,我們創(chuàng)建的對象明明是可變的,為什么在改變的時(shí)候卻因?yàn)樵搶ο蟛豢筛淖兌罎⒛匮每科鋵?shí)這就是copy的貢獻(xiàn)了抬吟。接下來我們輸出他們賦值之后的類型你就會(huì)明白了,看下圖统抬。

驗(yàn)證.png

看到了嗎火本?經(jīng)過賦值之后strong修飾的對象依然是NSMutableString危队,然而copy修飾的對象卻變成了NSString,這是為什么呢钙畔?
其實(shí)茫陆,這是因?yàn)樗械倪@種可變類型都是繼承對應(yīng)的不可變類型,然而刃鳄,卻沒有重寫不可變類型的copy方法盅弛,當(dāng)使用copy修飾的時(shí)候,調(diào)用不可變類型的copy方法叔锐,得到的其實(shí)是一個(gè)不可變的對象挪鹏,此時(shí),我們?nèi)バ薷乃淅樱匀痪蜁?huì)報(bào)錯(cuò)了讨盒。

結(jié)尾:

最近在看唐巧大神和故胤道長一起寫的《iOS面試之道》和《Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理》這兩本書,突然心血來潮步责,以前沒仔細(xì)研究過返顺,這次就研究了一下,把不定期更新的博客更新一下蔓肯,有用你就給個(gè)喜歡遂鹊,沒用就當(dāng)看個(gè)熱鬧,想認(rèn)識(shí)交流技術(shù)的就關(guān)注一下蔗包,關(guān)注我的我也會(huì)關(guān)注你哦秉扑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市调限,隨后出現(xiàn)的幾起案子舟陆,更是在濱河造成了極大的恐慌,老刑警劉巖耻矮,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秦躯,死亡現(xiàn)場離奇詭異,居然都是意外死亡裆装,警方通過查閱死者的電腦和手機(jī)踱承,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哨免,“玉大人勾扭,你說我怎么就攤上這事√鳎” “怎么了妙色?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長慧耍。 經(jīng)常有香客問我身辨,道長丐谋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任煌珊,我火速辦了婚禮号俐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘定庵。我一直安慰自己吏饿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布蔬浙。 她就那樣靜靜地躺著猪落,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畴博。 梳的紋絲不亂的頭發(fā)上笨忌,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音俱病,去河邊找鬼官疲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛亮隙,可吹牛的內(nèi)容都是我干的途凫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼溢吻,長吁一口氣:“原來是場噩夢啊……” “哼维费!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起煤裙,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對情侶失蹤掩完,失蹤者是張志新(化名)和其女友劉穎噪漾,沒想到半個(gè)月后硼砰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡欣硼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年题翰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诈胜。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豹障,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焦匈,到底是詐尸還是另有隱情血公,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布缓熟,位于F島的核電站累魔,受9級(jí)特大地震影響摔笤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垦写,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一吕世、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧梯投,春花似錦命辖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至咆疗,卻和暖如春漓帚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背午磁。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工尝抖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迅皇。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓昧辽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親登颓。 傳聞我的和親對象是個(gè)殘疾皇子搅荞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,373評(píng)論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,097評(píng)論 1 32
  • 本文邏輯圖: 在知道他們區(qū)別之前,我們首先要知道NSObject對象的賦值操作做了哪些操作框咙。 A=C其實(shí)是在內(nèi)存中...
    壯了個(gè)壯閱讀 20,805評(píng)論 40 148
  • 面試題參考1 : 面試題[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios閱讀 1,735評(píng)論 0 4
  • 楊孜 你是活在無量山上的那只 兇猛的豹子吧咕痛?!母獸們用低吟淺唱 為你伴奏喇嘱,晚風(fēng)屏住鼻息圍著你游走 我似乎聽到了茶盞...
    楊孜閱讀 476評(píng)論 0 0