精通iOS開發--第15章 Grand Central Dispatch和後臺處理之Block與Self的迴圈引用
Block與Self的迴圈引用
01:眾所周知若self中引用了Block塊,而此Block塊中又引用了Self則會造成迴圈引用,需要提醒的是即使在你的block程式碼中沒有顯式地出現"self",也有可能出現迴圈引用!只要你在block裡用到了self所擁有的東西就有可能導致迴圈引用。如下面的程式碼就會造成迴圈引用,測試發現BlockTest物件使用後沒有正常釋放。
@interfaceBlockTest()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) void(^myBlock)(
@end
- (instancetype)init
{
self = [superinit];
if (self) {
self.name = @"BlockTest";
self.myBlock = ^{
NSLog(@"%@",_name);
};
self.myBlock();
}
returnself;
}
-(void)dealloc
{
NSLog(@"dealloc");
#if __has_feature(objc_arc)
NSLog(
#else
[superdealloc];
NSLog(@"MRC %s",__func__);
#endif
}
若改成以下程式碼則可以正常釋放:
- (instancetype)init
{
self = [superinit];
if (self) {
self.name = @"BlockTest";
NSString* str = self.name;
self.myBlock = ^{
//NSLog(@"%@",_name);
NSLog(@"%@",str);
};
self.myBlock();
}
returnself;
}
02:為了解決迴圈引用問題:
在ARC:可以使用
__weak BlockTest *weakSelf = self;
或者
__unsafe_unretained BlockTest *unsafeSelf = self;
MRC:
__block BlockTest *blockSelf = self;
使用__unsafe_unretained也可以。
注意:MRC中__block是不會引起retain;但在ARC中__block則會引起retain。ARC中應該使用__weak 或__unsafe_unretained 弱引用。__weak只能在iOS5以後使用。通過測試會發現,在ARC中加了__weak和__unsafe_unretained的變數引入後,BlockTest可以正常執行dealloc方法,而不轉換和用__block轉換的變數都會引起迴圈引用。
03:驗證02結論的測試程式碼:
#import <Foundation/Foundation.h>
typedefvoid(^blockT)();
@interface BlockTest : NSObject
@property (nonatomic,copy) blockT block;
@end
//———————————————
#import "BlockTest.h"
@interfaceBlockTest()
@property (nonatomic,strong) NSString *name;
@end
@implementation BlockTest
- (instancetype)init
{
self = [superinit];
if (self) {
__blocktypeof(self) weekSelf = self;
self.block = ^{
weekSelf.name = @"BlockTest";
NSLog(@"%@",weekSelf.name);
};
}
returnself;
}
-(void)dealloc
{
NSLog(@"%s",__func__);
}
@end
測試:
- (void)viewDidLoad {
[superviewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
BlockTest *test = [[BlockTestalloc]init];
NSLog(@"%p",test);
}
通過類似的測試可以發現
MRC下面使用__unsafe_unretained和__block都可以避免這個問題(MRC下不能使用__weak關鍵字)。
ARC下使用__unsafe_unretained和__weak可以免。
04:看下面程式碼:
//MRC 環境下,下面的程式碼會崩潰,則至少可以說明MRC下,Block塊對__block修飾的date沒有進行保留,所以sleep(2.0)後,再使用date時造成了崩潰。
- (void)blockTest
{
__blockNSDate *date = [NSDatedate];
NSLog(@"%p %lu %@",date,[date retainCount],date);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %lu %@",date,[date retainCount],date);
});
NSLog(@"%p %lu %@",date,[date retainCount],date);
}
//ARC 環境下,下面的程式碼編譯時會報錯,date不可以在block中修改。
- (void)blockTest
{
NSDate *date = [NSDatedate];
NSLog(@"%p %@",date,date);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %@",date,date);
date = [NSDate date];
NSLog(@"%p %@",date,date);
});
NSLog(@"%p %@",date,date);
}
//ARC 環境下,下面的程式碼不會報錯也不會崩潰,date可以在block中修改,這至少可以說明sleep(2.0)後date依然存在。
- (void)blockTest
{
__blockNSDate *date = [NSDatedate];
NSLog(@"%p %@",date,date);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %@",date,date);
date = [NSDatedate];
NSLog(@"%p %@",date,date);
});
NSLog(@"%p %@",date,date);
}
/*
2016-06-25 17:01:57.505 GCD[2125:284060] 0x7fab93c20c10 2016-06-25 09:01:56 +0000
2016-06-25 17:01:58.056 GCD[2125:284060] 0x7fab93c20c10 2016-06-25 09:01:56 +0000
2016-06-25 17:02:00.059 GCD[2125:284200] 0x7fab93c20c10 2016-06-25 09:01:56 +0000
2016-06-25 17:02:00.822 GCD[2125:284200] 0x7fab93eaeb80 2016-06-25 09:02:00 +0000
*/
-(void)dealloc
{
#if __has_feature(objc_arc)
#else
[superdealloc];
#endif
NSLog(@"%s",__func__);
}
//MRC,使用下面的方式就可以解決MRC下程式崩潰的問題。因為date2沒有被__block修飾,所以其生命週期一直保留到block塊執行結束。
- (void)blockTest
{
__blockNSDate *date = [NSDatedate];
NSLog(@"%p %@",date,date);
NSDate *date2 = date;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %@",date2,date2);
date = [NSDatedate];
NSLog(@"%p %@",date,date);
});
NSLog(@"%p %@",date,date);
}
05:下面來討論一下用__block修飾普通的C變數會如何:
如果一個程式塊在執行過程中訪問任何“外部”變數,那麼該程式塊被建立時,會進行一些特殊的設定工作,以允許程式塊訪問那些變數。這些變數所包含的值要麼被複制(如一些普通的C型別變數),要麼被儲存(如OC物件),這樣他們所包含的值就可以在程式塊內部使用了(被生命週期修飾符修飾的變數,要分ARC、MRC等具體討論)。
但如果想讓一個程式塊向一個外部定義的變數寫入資料,這時用__block儲存修飾符修飾這個變數即可。一個有趣的副作用是:__block修飾的變數(在MRC中)在程式塊中使用時不會被複制(看下面的例子會發現,普通的C型別變數會被複制到堆中)或者保留(如上面的示例程式,在MRC下Block修飾的變數並沒有被保留)。
//ARC,下面的程式不會崩潰。
- (void)blockTest
{
int intt2 = 200;
NSLog(@"1:%p",&intt2);
__blockint intt = 100;
NSLog(@"2:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"3:%p %i",&intt,intt);
intt = 200;
NSLog(@"4:%p %i",&intt,intt);
});
int *inta = malloc(sizeof(int));
NSLog(@"5:%p",inta);
free(inta);
NSLog(@"6:%p %i",&intt,intt);
}
2016-06-25 17:38:06.765 GCD[2742:304212] 1:0x7fff50a02a8c
2016-06-25 17:38:11.063 GCD[2742:304212] 2:0x7fff50a02a80 100 //可以看出intt、intt2這兩個都是在棧上面分配的空間
2016-06-25 17:38:15.624 GCD[2742:304212] 5:0x7f93da408e80
2016-06-25 17:38:15.624 GCD[2742:304253] 3:0x7f93da535ef8 100
2016-06-25 17:38:17.211 GCD[2742:304212] 6:0x7f93da535ef8 100
2016-06-25 17:38:29.243 GCD[2742:304253] 4:0x7f93da535ef8 200 //可以看出intt被在堆中重新複製了一遍,甚至在block塊外面其地址也與開始時不同(下面還會討論這種情況何時才會發生)。
注意看下面的例子:
//MRC,下面的程式同樣不會崩潰。
- (void)blockTest
{
int intt2 = 200;
NSLog(@"1:%p",&intt2);//這兩個都是在棧上面分配的空間
__blockint intt = 100;
NSLog(@"2:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"3:%p %i",&intt,intt);
intt = 200;
NSLog(@"4:%p %i",&intt,intt);
});
int *inta = malloc(sizeof(int));
NSLog(@"5:%p",inta);
free(inta);
NSLog(@"6:%p %i",&intt,intt);
}
2016-06-25 17:42:08.125 GCD[2857:307005] 1:0x7fff5dc39a8c
2016-06-25 17:42:19.124 GCD[2857:307005] 2:0x7fff5dc39a80 100
2016-06-25 17:42:30.435 GCD[2857:307005] 5:0x7fc782554960
2016-06-25 17:42:35.086 GCD[2857:307005] 6:0x7fc78240ab38 100
2016-06-25 17:42:35.086 GCD[2857:307395] 3:0x7fc78240ab38 100
2016-06-25 17:42:43.869 GCD[2857:307005] MRC -[BlockTest dealloc]
2016-06-25 17:42:46.275 GCD[2857:307395] 4:0x7fc78240ab38 200
MRC中intt同樣在堆中被重新複製了一遍。結和self與block迴圈引用的討論,可以得出如下結果:
MRC下 block塊外面用__block修飾的變數,若是OC物件,則不會retain,C型別變數會在堆中重新分配
ARC下 block塊外面用__block修飾的變數,若是OC物件,會retain,C型別變數會在堆中重新分配。
06://ARC 注意看記憶體分配,與__block修飾時還是有很大不同
- (void)blockTest2
{
int intt = 100;
NSLog(@"1:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"2:%p %i",&intt,intt);
//intt = 200;
NSLog(@"3:%p %i",&intt,intt);
});
NSLog(@"4:%p %i",&intt,intt);
}
2016-06-25 17:59:46.987 GCD[3137:315124] 0x7f8beb693f30
2016-06-25 17:59:48.794 GCD[3137:315124] 1:0x7fff5b27ca8c 100
2016-06-25 17:59:48.795 GCD[3137:315124] 4:0x7fff5b27ca8c 100
2016-06-25 17:59:50.800 GCD[3137:315194] 2:0x7f8beb611820 100
2016-06-25 17:59:50.800 GCD[3137:315194] 3:0x7f8beb611820 100
用__block修飾時4與2和3列印的地址是相通的,沒有其修飾時1與4相同,2與3相同。
再看MRC:
- (void)blockTest2
{
相關推薦
精通iOS開發--第15章 Grand Central Dispatch和後臺處理之Block與Self的迴圈引用
Block與Self的迴圈引用 01:眾所周知若self中引用了Block塊,而此Block塊中又引用了Self則會造成迴圈引用,需要提醒的是即使在你的block程式碼中沒有顯式地出現"s
第1章-資料探索(3)-資料預處理之R實現
簡介 R語言中,自身已經帶有了強大的資料處理、資料計算等方面的函式。 雖然,對於大規模的資料集合,處理過程可能會不如Python快,但是小規模的資料處理,R語言使用起來仍然會更方便。 值得注意的是,為了執行效率,我們要儘量避免在R語言中,使用迴圈函式,而是要運用向量化的處理函式,即R
第1章-資料探索(2)-資料預處理之Python實現
簡介 Python中,在資料處理這方面最流行的包應當是屬於Pandas了。Pandas與Scipy一樣,都是基於NumPy這個包開發出來的,所以使用時,都需要引用Numpy。Pandas中的DataFrame與R語言中的資料框的設計理念基本是一致的。不光如此,除了是DataFrame資料
iOS開發之再探多執行緒程式設計:Grand Central Dispatch詳解
Swift3.0相關程式碼已在github上更新。之前關於iOS開發多執行緒的內容釋出過一篇部落格,其中介紹了NSThread、操作佇列以及GCD,介紹的不夠深入。今天就以GCD為主題來全面的總結一下GCD的使用方式。GCD的歷史以及好處在此就不做過多的贅述了。本篇部落格會通過一系列的例項來好好的總結一下GC
python編程快速上手之第15章實踐項目參考答案(17.7.2)
col ges code sid documents mod 編程 bsp tof #! python3 # Import modules and write comments to describe this program. import zipfile, os fr
python編程快速上手之第15章實踐項目參考答案(17.7.3)
lane width ima font height 開始 users nco window #! python3 # encoding: UTF-8 import os,docx from PIL import Image, ImageDraw from PIL imp
Java開發工程師(Web方向) - 03.數據庫開發 - 第1章.JDBC
class java類庫 nload 個數 註冊 化工 建立數據庫 get tin 第1章--JDBC JDBC基礎 通過Java Database Connectivity可以實現Java程序對後端數據庫的訪問 一個完整的數據庫部署架構,通常是由客戶端和服務器端兩部分組成
Java開發工程師(Web方向) - 03.數據庫開發 - 第4章.事務
完成 工作單元 ima auto 增加 並發執行 trace driver transfer 第4章--事務 事務原理與開發 事務Transaction: 什麽是事務? 事務是並發控制的基本單位,指作為單個邏輯工作單元執行的一系列操作,且邏輯工作單元需滿足ACID特性。
C後端設計開發 - 第6章-武技-常見組件上三路
錯誤 design 謝大 pos cde strong com wan .com 正文 第6章-武技-常見組件上三路 後記 如果有錯誤, 歡迎指正. 有好的補充, 和疑問歡迎交流, 一塊提高. 在此謝謝大家了. C後端設計開發 - 第6章-武技-常
C後端設計開發 - 第7章-真氣-遺失的網絡IO
com itl ron alt book blank nbsp 如果 tree 正文 第7章-真氣-遺失的網絡IO 後記 如果有錯誤, 歡迎指正. 有好的補充, 和疑問歡迎交流, 一塊提高. 在此謝謝大家了. ボクらの冒
第15章:字符串
用戶輸入 內存 拆分 系列 提取 string length java開發 trim 第15章:字符串 概念 是一系列字符組成的序列 使用字符串 1.定義並初始化字符串、 2.使用字符串,對字符串進行一些處理 字符串長度 語法 字符串名.length(); 語法 語法1:字
iOS多線程編程(四)------ GCD(Grand Central Dispatch)
execution 使用 att 意義 pro num patch 任務並發 comm 一、簡單介紹 是基於C語言開發的一套多線程開發機制。也是眼下
Linux命令應用大詞典-第 15章 文件、目錄權限和屬性
pos pan span 所有 -c get hat 5.4 屬性 15.1 chmod:更改文件和目錄的模式 15.2 chown:更改文件和目錄的用戶所有者和組群所有者 15.3 chgrp:更改文件或目錄的所屬組 15.4 umask:顯示和設置文件及目錄創建默認權
第15章WEB15-AJAX和JQuery案例篇
AJAX和JQuery案例篇 javaweb 今日任務? 使用AJAX完成用戶名的異步校驗? 使用JQuery完成用戶名異步校驗? 使用JQuery完成商品信息模糊顯示? 使用JQuery完成省市聯動效果返回XML? 使用JQuery完成省市聯動效果返回JSON教學導航教學目標了解AJAX的基本使用掌
精通iOS開發》書籍目錄
ios 浮動 攝像 graphic 後臺 rand isp IT 基本 1、歡迎來到iOS和Swift世界 2、創建一個新項目 3、實現基本交互 4、更豐富的用戶界面 5、自動旋轉和自動調整大小 6、多視圖應用 7、分頁欄與選取器 8、表視圖簡介 9、導航控制器
【C++ Primer 第15章】抽象基類
*** rim ica include margin urn 對象 class mes 抽象基類 【註意】我們也可以為純虛函數提供定義,不過函數體必須在類的外部,也就是說,我們不能再內部為一個=0思網函數提供函數體。 C++中含有(或未覆蓋直接繼承)純虛函數的類是抽象
Excel企業實戰與解決方案開發第2章發布
Excel;VBA;公式;財務;人事《Excel企業實戰與解決方案開發》系列課程的第2章——公式函數篇已發布,想學Excel或VBA的註意啦~ 與第1章一樣,本課程包含了大量其他培訓未提及的更專業的實用技能,適合各種級別的用戶(包括會VBA的高級用戶,但不包括非常初級的用戶)學習。 另外這一章很多內容是接著
《Think Python》第15章學習筆記
用戶 main asa ... 類名 執行 __main__ deb 5.4 《Think Python》第15章學習筆記 [TOC] 15.1 程序員定義的類型(Programmer-defined types) 定義一個類的過程會創建一個類型對象(type object
【C++ Primer 第15章】定義派生類拷貝賦值運算符
運算符 結果 類成員變量 you 輸出 ons c++ prime get pre 學習資料 • 派生類的賦值運算符/賦值構造函數也必須處理它的基類成員的賦值 定義賦值運算符 【註意】對派生類進行拷貝構造時,如果想讓基類的成員也同時拷貝,就一定要在派生類拷貝
【二代示波器教程】第15章 FreeRTOS操作系統版本二代示波器實現
per lamp 轉換 length 失去 最大值 ucd 參數 state 第15章 FreeRTOS操作系統版本二代示波器實現 本章教程為大家講解FreeRTOS操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,