白話block

本文內(nèi)容

  • 什么是block?
  • block的用途
  • block的用法
  • block在使用中遇到的問題
  • 如何使用xcode檢測循環(huán)引用引發(fā)的內(nèi)存問題?

什么是block?

什么是閉包(closure)?
閉包是可以包含自由(未綁定到特定對象)變量的代碼塊。
什么是函數(shù)式編程?
函數(shù)可以隨時創(chuàng)建、作為參數(shù)傳遞、作為返回值返回。
在函數(shù)式編程中,把函數(shù)當(dāng)參數(shù)來回傳遞,而這個,說成術(shù)語,我們把他叫做高階函數(shù)。
Objective-C在沒有block之前,沒有類似的機(jī)制,有了block,Objective-C也就具備了函數(shù)式編程的能力,block是對象,有自己的ISA指針,可以隨時創(chuàng)建,作為參數(shù)傳遞,作為返回值返回
block是帶有局部變量的匿名函數(shù)(即沒有名稱的函數(shù)),就是OC中的閉包(closure),又名匿名函數(shù),塊函數(shù),塊

block的用途

block都是一些簡短代碼片段的封裝,適用作工作單元,通常用來做并發(fā)任務(wù)、遍歷、以及回調(diào)。

block的用法

  1. block 語法,用插入符號"^"定義一個block字面量,無參,無返回值的block 如下

^{
NSLog(@"This is A Block");
}();

控制臺輸出:

2015-11-03 12:04:26.266 Whisper[63849:545630] This is a block

  1. block的類型(可以用isa 指針指向的地址查看)
  • NSConcreteGlobalBlock 全局的靜態(tài) block,不會訪問任何外部變量。
  • NSConcreteStackBlock 保存在棧中的 block,當(dāng)函數(shù)返回時會被銷毀。
  • NSConcreteMallocBlock 保存在堆中的 block,當(dāng)引用計數(shù)為 0 時會被銷毀。
  • 在 ARC 開啟的情況下,將只會有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的 block
  1. block Pointer,主要用來簡化block的寫法,block Pointer定義如下:

回傳值 (^名字)(參數(shù)列表);

block Pointer 具體使用

int (^myBlock) (int a); // 聲明一個名字為myBlock的block 指針,該指針指向的Block有一個int輸入和一個int 輸出
myBlock = ^(int a){ return a*a;}; //將Block的實體指定給myBlock指針
int result = myBlock(4); //調(diào)用blcok實體

  1. 使用typedef給復(fù)雜變量block定義類型別名,定義規(guī)則:
    在傳統(tǒng)的變量聲明表達(dá)式里用類型名替代變量名,然后把關(guān)鍵字typedef加在該語句的開頭,示例如下:

typedef int (^myBlock) (int a); //使用定義的新類型myBlock來聲明對象,等價于int (^myBlock) (int a);

一般借助block的類型別名,為特定的類添加block屬性變量,做傳值或者事件使用。聲明block屬性變量的時候,property中需設(shè)置成copy

  1. 用block來存取變量
    一個Block的內(nèi)部是可以引用自身作用域外的變量的,包括static變量,extern變量或自由變量,對于自由變量,在Block中是只讀的。在引入block的同時,還引入了一種特殊的__block關(guān)鍵字,變量存儲修飾符,將變量的存儲范圍擴(kuò)展為該函數(shù)以及該函數(shù)內(nèi)定義的block的行為主體內(nèi)(蘋果官方文檔)。
    存取靜態(tài)變量

static int outA = 8;

int (^myPtr)(int) = ^(int a){return outA + a;};  
outA = 5;  
int result = myPtr(3);        //result的值是8,因為outA是static類型的變量

存取自由變量

{
__block int num = 5;
int (^myPtr)(int) = ^(int a){return num++;};
int (^myPtr2)(int) = ^(int a){return num++;};
int result = myPtr(0); //result的值為5,num的值為6
result = myPtr2(0); //result的值為6,num的值為7
NSLog(@"result=%d", result);
}

  1. block作為類的屬性

typedef int (^MyBlock) (int a);
@property(nonatomic,copy) int (^block)(int a); //使用c的方式, 不能使用OC函數(shù)形參的寫法.
@property (nonatomic,copy) MyBlock blockName;

  1. block作為函數(shù)參數(shù),蘋果官方文檔建議一個方法最好只有一個block參數(shù),并且block參數(shù)一般作為最后一個參數(shù)

- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock {
...
callbackBlock();
}
- (void)beginFetchWithCallbackBlock:(MyBlock)callbackBlock {
...
callbackBlock(3);
}

  1. 以block作為函數(shù)返回值

-(int (^)(int))blockBack{
return ^(int cout){ return cout;};
}

block在使用中遇到的問題

  1. 修改局部變量需要在局部變量前面加__block修飾符,將變量的存儲范圍擴(kuò)展為該函數(shù)以及該函數(shù)內(nèi)定義的block的行為主體內(nèi)。
  2. 在屬性定義一個block的時候需要使用copy,因為塊是在棧上分配的,一旦離開作用域, 就會釋放, 因此如果你要把塊用在別的地方, 必須要復(fù)制一份
  3. 在ARC下, 以下幾種情況, Block會自動被從棧復(fù)制到堆
  • 被執(zhí)行copy方法
  • 作為方法返回值
  • 將Block賦值給附有__strong修飾符的id類型的類或者Blcok類型成員變量時
  • 在方法名中含有usingBlock的Cocoa框架方法或者GCD的API中傳遞的時候.
  1. 循環(huán)引用的問題
  • A和B兩個對象,A持有B,B同時也持有A,A只有B釋放之后才有可能釋放,同樣B只有A釋放后才可能釋放,當(dāng)雙方都在等待對方釋放的時候, retain cycle就形成了,結(jié)果是,兩個對象都永遠(yuǎn)不會被釋放,最終內(nèi)存泄露。
  • 循環(huán)引用(retain cycle)的解決
  • 盡量保持子對象引用父對象的時候使用弱引用,也就是assign,比如

@property (nonatomic,assign) NSObject *parent;

  • 及時地將造成retain cycle中的一個變量設(shè)置為nil,將環(huán)break掉
  • block中的retain cycle

@interface XYZBlockKeeper : NSObject
@property (copy) void (^block)(void);
@implementation XYZBlockKeeper
- (void)configureBlock {
self.block = ^{
[self doSomething]; // capturing a strong reference to self
// creates a reference cycle
};
}
...
@end

  • block中retain cycle 的解決

    • 方法一 將引用的一方變成weak,從而避免循環(huán)引用

- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // capture the weak reference
// to avoid the reference cycle
}
}
或者
- (void)configureBlock {
__weak typeof(self) weakSelf = self;
self.block = ^{
//如果想防止 weakSelf 被釋放,可以再次強(qiáng)引用
typeof(weakSelf) strongSelf = weakSelf;

  [weakSelf doSomething];   // capture the weak reference cycle

}
}

  • 方法二.使用完某對象沒有必要在保留該對象的時候,在block里面將對象釋放即可打破保留環(huán)

- (void)downloadData {
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
_networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];
[_networkFetcher startWithCompletionHandler:^(NSData *data) {
_fetchedData = data;
_networkFetcher = nil; //加上此行,此處是為了打破循環(huán)引用
}];
}

  • 方法三. 在調(diào)用完block之后,將該block設(shè)置為nil(block為某類的屬性的時候,這么使用)

- (void)p_requestCompleted {
if(_completionHandler) {
_completionHandler(_downloadData);
}
self.completionHandler = nil;//加上此行,此處是為了打破循環(huán)引用
}

如何使用xcode檢測循環(huán)引用(引用自《iOS開發(fā)進(jìn)階》)?

  1. Xcode 的Instruments工具集可以很方便地檢測循環(huán)引用,但是檢測不出block產(chǎn)生的循環(huán)引用,示例如下

- (void)viewDidLoad {
[super viewDidLoad];
//firstArray 持有secondArray, secondArray 持有 firstArray,形成retain cycle
NSMutableArray *firstArray = [NSMutableArray array];
NSMutableArray *secondArray = [NSMutableArray array];
[firstArray addObject:secondArray];
[secondArray addObject:firstArray];
}

  1. 在Xcode 的菜單欄,選擇“Product”--->“Profile”,在調(diào)出的界面中選擇"Leaks"--->"choose",調(diào)出Instruments界面。
    Instruments會用紅色的X表示一次內(nèi)存泄露的產(chǎn)生,Instruments中可以通過切換到“Leaks”,單擊“Cycles&Roots”,就可以看到以圖形方式顯示出來的循環(huán)引用,這樣,我們就可以很方便的看到產(chǎn)生循環(huán)引用的對象了。
  • 具體使用步驟如下:


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

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

  • Block使用場景,可以在兩個界面的傳值,也可以對代碼封裝作為參數(shù)的傳遞等。用過GCD就知道Block的精妙之處。...
    Coder_JMicheal閱讀 733評論 2 1
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴(kuò)展,用來實現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,363評論 2 26
  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,778評論 0 23
  • Block簡介(copy一段) Block作為C語言的擴(kuò)展,并不是高新技術(shù),和其他語言的閉包或lambda表達(dá)式是...
    qui丶MyLove閱讀 435評論 0 0
  • 不愿提及的你: 嗨!老同學(xué),如果有一天你看到這封信會不會感到驚訝呢?其實,當(dāng)我腦海里冒出這個想法的時候我被...
    會跳舞的華爾茲閱讀 376評論 0 3