iOS 內(nèi)存管理篇(三)---__strong/__weak/__unsafe_unretain/__autoreleasing 認識和使用

之前學習的文章鏈接

iOS內(nèi)存管理篇(一)---alloc/reatain/release/dealloc方法實現(xiàn)

iOS內(nèi)存管理篇(二)---NSAutoreleasePool/@autoreleasepool/autorelease理解與管理

前言:說到內(nèi)存管理,避免不了的就是循環(huán)應用和某個變量釋放的實際,雖然在實際開發(fā)種,ARC會為我們自動的加上引用技術和減少引用技術,但是并不是萬能的,百密一疏,還是會在實際開發(fā)過程中內(nèi)存管理出現(xiàn)問題,今天我們要來看的就是__strong,__weak,這些關鍵詞的作用和使用方法

__strong關鍵字與retain關似,用了它,引用計數(shù)自動+1

有如下 Demo:

id obj = [[NSObject alloc]init];

/*等價于*/

id __strong obj = [[NSObject alloc]init];

那么在內(nèi)存里面是怎么執(zhí)行這段代碼的呢

/*編譯器模擬的代碼*/
id obj = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(obj,@selector(init));
objc_release(obj);

從上面的代碼我們可以看出,系統(tǒng)兩次調(diào)用objc_msgSend來創(chuàng)建這個對象并為之開辟內(nèi)存空間,然后在變量作用于失效的時候使用objc_release來釋放對象,雖然在 ARC模式下我們不能使用 release,但是系統(tǒng)在運行時為我們自動添加了 release。

那么我們來看看實際的例子吧

有兩個變量,是用 strong 修飾的

@property (nonatomic, strong) NSString *str1;
@property (nonatomic, strong) NSString *str2;

做了如下的操作

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

大家可以猜猜打印結(jié)果是什么

打印結(jié)果是 self.str2 = A

由于str2是strong定義的屬性,所以引用計數(shù)+1,使得它們所指向的值都是@"A", 雖然 self.str = nil 了,但是并沒有使得 str2的引用計數(shù)減1導致釋放,所以打印的結(jié)果為 A


__weak修飾符提供的功能如同魔法一般

  • 若附有__weak修飾符的變量所引用的對象被廢棄,則將 nil賦值給該變量

  • 使用附有__weak修飾符的變量,即是使用注冊到__autoreleasepool 中的對象

有如下代碼:

id __strong obj = [[NSObject alloc]init];
id __weak obj1 = obj;

我們看看系統(tǒng)是如何為我們處理的

/*編譯器模擬的代碼*/
id obj1;
objc_initWeak(&obj1,obj);
objc_destroyWeak(&obj1);

通過objc_initWeak函數(shù)初始化附有__weak修飾符的變量,在變量作用域結(jié)束的時候用objc_destroyWeak釋放該變量

我們來下一下的具體的例子

@property (nonatomic, strong) NSString *str1;
@property (nonatomic, weak)   NSString *str2;

執(zhí)行如下的代碼

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

結(jié)果是 self.str2 = null 為什么會是這個結(jié)果呢 分析一下,由于self.string1與self.str2指向同一地址,且str2沒有retain內(nèi)存地址,而 self.str1=nil釋放了內(nèi)存,所以string1為nil。
所以這是符合第一條規(guī)則的,因為str2 指向的地址已經(jīng)被釋放了,那么會將 nil 賦值給 str2

在這個過程中__weak幫我們干了什么呢

(1) 從 weak表中獲取廢棄對象的地址為鍵值的記錄
??(2) 將包含在記錄中的所有附有__weak 修飾變量的地址,賦值為 nil
??(3) 從 weak 表中刪除該記錄
??(4) 從引用技術表中刪除廢棄對象的地址為鍵值的記錄

我們來看看 weak 的另外一個特性

id __weak obj1 = obj/*obj 是 strong 的*/
NSLog(%@"obj1 = %@",obj1);

這段代碼轉(zhuǎn)換為系統(tǒng)編譯器碼為

id obj1;
objc_initWeak(&obj1,obj);
id temp = objc_loadWeakRetained(&obj1);
objc_autorelease(temp);
NSLog(%"%@",temp);
objc_destroyWeak(obj1);

我們可以注意到有個objc_autorelease這個函數(shù),所以正說明了第二點:使用附有__weak修飾符的變量,即是使用注冊到__autoreleasepool 中的對象,因為附有__weak的變量所使用的對象都被注冊到了__autoreleasepool里面,所以在@autoreleasepool塊結(jié)束之前可以放心使用,所以在實際開發(fā)過程中,如果大量使用 weak 來修飾變量的話,是會非常耗費 CPU資源的,所以當只有會可能產(chǎn)生循環(huán)引用的地方使用 weak 是合適的。


__unsafe_unretain顧名思義就是不安全的,其功能和 weak 差不多

有如下的代碼

id __unsafe_unretain obj = [[NSObject alloc]init];

這時候編譯器報出警告,該代碼對應的模擬代碼為

/*編譯器模擬的代碼*/
id obj = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(obj,@selector(init));
objc_release(obj);

objc_release函數(shù)立即釋放了生成并持有的對象,這樣對象的懸垂指針被賦值給了變量obj 當中

我們來看如下的代碼:

@property (nonatomic, strong)             NSString *str1;
@property (nonatomic,__unsafe_unretain)   NSString *str2;

執(zhí)行如下的代碼

self.str1 = @"A";
self.str2 = self.str1;
self.str1 = nil;
NSLog(@"self.str2 = %@",self.str2);

這次根本就不用輸出,在沒有輸出前,程序已經(jīng)崩潰了,其實就是野指針造成的,為何會造成野指針呢?同于用unsafe_unretained聲明的指針,由于 self.str1=nil已將內(nèi)存釋放掉了,但是str2并不知道已被釋放了,所以是野指針。然后訪問野指針的內(nèi)存就造成crash. 所以盡量少用unsafe_unretained關鍵字。

__autoreleasing:將對象賦值給附有__autoreleasing修飾的變量等同于 ARC無效時調(diào)用對象的 autorelease 方法

有如下代碼:

@autoreleasepool{
     id __autoreleasing obj = [[NSObject alloc]init];   
}

對應的編譯器碼:

/*編譯器的模擬代碼*/
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(obj,@selector(init));
objc_autorelease(obj);
objc_autoreleasePoolPop(pool);
- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{
    NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];
    NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];
    NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
    *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
}
-(void)test
{
    NSError *error = nil;
    [self generateErrorInVariable:&error];
    NSLog(@"Error = %@", error);
}

__autoreleasing則可以使對像延遲釋放。比如你想傳一個未初始 化地對像引用到一個方法當中,在此方法中實始化此對像,那么這種情況將是__autoreleasing表演的時候。看個示例:

@autoreleasepool{
     id __autoreleasing obj = [[NSObject alloc]init];   
}

對應的編譯器碼:

- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{
    NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];
    NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];
    NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
    *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
}
-(void)test
{
    NSError *error = nil;
    [self generateErrorInVariable:&error];
    NSLog(@"Error = %@", error);
}

符合內(nèi)存管理規(guī)則:誰分配誰釋放。

小結(jié):不管是__strong、__weak、__unsafe_unretain、__autoreleasing 這是為了讓我們更好的理解內(nèi)存管理,而且在必要的時候使用這些修飾符來管理我們的內(nèi)存,而在實際開發(fā)種,一般很少使用到后兩者,前兩者在循環(huán)引用的時候可以用來解循環(huán)引用,我們在今后的學習中還會學習和使用到它們,別忘了喲~

此文借鑒的文章鏈接
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing區(qū)別和作用詳解
書籍《Objective-C高級編程 iOS與OS X多線程和內(nèi)存管理》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

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