1. 程式人生 > >精通iOS開發--第15章 Grand Central Dispatch和後臺處理之Block與Self的迴圈引用

精通iOS開發--第15章 Grand Central Dispatch和後臺處理之Block與Self的迴圈引用

BlockSelf的迴圈引用

01:眾所周知若self中引用了Block塊,而此Block塊中又引用了Self則會造成迴圈引用,需要提醒的是即使在你的block程式碼中沒有顯式地出現"self",也有可能出現迴圈引用!只要你在block裡用到了self所擁有的東西就有可能導致迴圈引用。如下面的程式碼就會造成迴圈引用,測試發現BlockTest物件使用後沒有正常釋放。

@interfaceBlockTest()

@property (nonatomic,copy) NSString *name;

@property (nonatomic,copy) void(^myBlock)(

void);

@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(

@"ARC    %s",__func__);

#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則會引起retainARC中應該使用__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物件),這樣他們所包含的值就可以在程式塊內部使用了(被生命週期修飾符修飾的變數,要分ARCMRC等具體討論)。

但如果想讓一個程式塊向一個外部定義的變數寫入資料,這時用__block儲存修飾符修飾這個變數即可。一個有趣的副作用是:__block修飾的變數(在MRC中)在程式塊中使用時不會被複制(看下面的例子會發現,普通的C型別變數會被複制到堆中)或者保留(如上面的示例程式,在MRCBlock修飾的變數並沒有被保留)

//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     //可以看出inttintt2這兩個都是在棧上面分配的空間

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

MRCintt同樣在堆中被重新複製了一遍。結和selfblock迴圈引用的討論,可以得出如下結果:

MRC block塊外面用__block修飾的變數,若是OC物件,則不會retainC型別變數會在堆中重新分配

ARC block塊外面用__block修飾的變數,若是OC物件,會retainC型別變數會在堆中重新分配。

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修飾時423列印的地址是相通的,沒有其修飾時14相同,23相同。

再看MRC

- (void)blockTest2

{

相關推薦

精通iOS開發--15 Grand Central Dispatch後臺處理BlockSelf迴圈引用

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:顯示和設置文件及目錄創建默認權

15WEB15-AJAXJQuery案例篇

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設計框架,即各個任務實現的功能,任務間的通信方案選擇,