目錄
一您机、通常作用;
二年局、block用來解決什么問題际看?
三、為什么要用copy修飾Block矢否;
四仲闽、為什么Block中可以訪問局部變量而不能修改;
五僵朗、為什么用__block修飾的局部變量可以在Block內(nèi)部修改赖欣;
六、為什么使用Block會造成循環(huán)引用验庙,怎么解決顶吮;
在iOS4.0之后,蘋果開始引入的對C語言的擴展粪薛,對于用法我們已經(jīng)相當(dāng)?shù)氖煜ゃ擦耍谖覀兪褂肁SI的時候還是delegate橫行,切換到AFNetWorking之后违寿,block開始被大量的使用湃交。
一、通常被用來做并發(fā)任務(wù)藤巢、遍歷和回調(diào)搞莺。
并發(fā)任務(wù):在線程管理方面由NSThead->NSOperation->GCD
遍歷:forin -> enumerateObjectsUsingBlock:
回調(diào):delegate,KVO掂咒,NSNotification->block
二才沧、那么block到底是用來做什么的呢迈喉?解決什么問題呢?
讓我們先來了解一下匿名函數(shù)
匿名函數(shù):就是沒有名字的函數(shù)温圆。
匿名函數(shù)最大的用途是創(chuàng)建閉包(這是JavaScript語言的特性之一)弊添,并且還可以構(gòu)建命名空間,以減少全局變量的使用捌木。
那么看看block的使用 -(void)doSomethingWithBlock:(void^(Bool success))block;
使用這個方法,其實也使用了一個回調(diào)函數(shù)嫉戚,block是一個匿名函數(shù)刨裆。
那么閉包是什么意思呢?
閉包就是函數(shù)的嵌套彬檀,內(nèi)層的函數(shù)可以使用外層函數(shù)的所有變量帆啃,即使外層函數(shù)已經(jīng)執(zhí)行完畢。
閉包與對象之間的關(guān)系是什么窍帝?
那么我們先看一下官方文檔上怎么說努潘?
A closure lets you associate some data (the environment) with a function that operates on that data. This has obvious parallels to object oriented programming, where objects allow us to associate some data (the object's properties) with one or more methods.
Consequently, you can use a closure anywhere that you might normally use an object with only a single method.
譯文:
閉包允許你將某些數(shù)據(jù) (環(huán)境) 與操作該數(shù)據(jù)的函數(shù)關(guān)聯(lián)起來,這與面向?qū)ο缶幊逃忻黠@的相似之處坤学,其中的對象允許我們?nèi)⒁恍?shù)據(jù)關(guān)聯(lián)一個或者更多的方法疯坤。
因此, 你可以在通常只使用單個方法的對象的任何地方使用閉包。
閉包相當(dāng)(近似)于一個只有一個方法的對象深浮,只有一個公開方法压怠,而且成員數(shù)據(jù)幾乎全私有(閉包)的對象,自然是一個緊湊版的對象了飞苇。
閉包連貫性好菌瘫,比較緊湊,因此看起來代碼比較清晰布卡。block就是iOS補充的閉包特性雨让。
為什么這么做?
前面我們提到的delegate和NSNotification都能將外部函數(shù)的所有變量傳出來忿等,為何選用block栖忠?這就是block的優(yōu)勢,緊湊以及代碼清晰贸街。
block的做法:UIButton ?*button; ?button.clickBlock = ^{};
使用delegate的時候還需要聲明娃闲,在代理中另起一行來實現(xiàn),而block讓我們看到這個對象以及這個對象的操作匾浪。
上面是從代碼清晰方面來講的皇帮,下面是功能方面:
block的提出是為了在不同的對象間除了傳遞值之外還可以傳遞一個操作而提出的。
A:
[self doSomethingWithBlock:^{log(A) }];
- (void)doSomethingWithBlock:(void(^)(void))block{ B.block = block}
B:
block();調(diào)用后蛋辈,log出A属拾。
三将谊、為什么要用copy修飾Block
為什么要用copy,這是因為在MRC時期渐白,作為屬性的block在初始化時是被存放在靜態(tài)區(qū)的尊浓,這樣在使用時如果block內(nèi)有調(diào)用外部變量,那么block無法保留其內(nèi)存纯衍,在初始化的作用域內(nèi)使用并不會有什么影響栋齿,但一但出了block的初始化作用域,就會引起崩潰襟诸,使用copy可以將block的內(nèi)存推入堆中瓦堵,這樣讓其擁有保存調(diào)用的外部變量的內(nèi)存的能力。
大部分人都認(rèn)為block作為屬性在聲明時歌亲,只能用copy修飾菇用,而不可以用strong。其實用strong也是可以的陷揪。只是使用copy修飾block是MRC時期的遺留物惋鸥,這在MRC時期是至關(guān)重要的事情,但是使用ARC的現(xiàn)在悍缠,strong是可以代替的卦绣,只是一個習(xí)慣問題而已。
四飞蚓、為什么Block中可以訪問局部變量而不能修改
例子如下:
注: 通過clang命令將OC轉(zhuǎn)為C++代碼來查看一下Block底層實現(xiàn),clang命令使用方式為終端使用cd定位到main.m文件所在文件夾,然后利用clang -rewrite-objc main.m將OC轉(zhuǎn)為C++,成功后在main.m同目錄下會生成一個main.cpp文件
由此可知,在Block定義時便是將局部變量的值傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對局部變量進(jìn)行修改并不會影響B(tài)lock內(nèi)部的值,同時內(nèi)部的值也是不可修改的迎卤。
五、為什么用__block修飾的局部變量可以在Block內(nèi)部修改
添加__block修飾符之后的代碼如下:
可以看到并不是直接傳遞a的值了玷坠,而是把a的地址傳過去了蜗搔,所以在block內(nèi)部便可以修改到外面的變量了。
六八堡、為什么使用Block會造成循環(huán)引用樟凄,怎么解決
因為對象obj在Block被copy到堆上的時候自動retain了一次。因為Block不知道obj什么時候被釋放兄渺,為了不在Block使用obj前被釋放缝龄,Block retain了obj一次,在Block被釋放的時候挂谍,obj被release一次叔壤。
循環(huán)引用問題的根源在于Block和obj可能會互相強引用,互相retain對方口叙,這樣就導(dǎo)致了循環(huán)引用炼绘,最后這個Block和obj就變成了孤島,誰也釋放不了誰妄田。
解決辦法:
在ARC中沒有retain俺亮,retainCount的概念驮捍。只有強引用和弱引用的概念。當(dāng)一個變量沒有__strong的指針指向它時脚曾,就會被系統(tǒng)釋放东且。
參考以及使用了以下文章的研究: