Block使用注意

在項目中,我們經(jīng)常會用到block,但是說起block你真的會用嗎?你真的全都了解嗎?如果心里犯嘀咕的話,那么就往下看吧~~~>_<

說block之前,我們先了解一下內(nèi)存的存儲知識,即內(nèi)存分區(qū)的問題.一個由C/C++編譯的程序占用的內(nèi)存分為以下幾個部分:

1.棧(stack) 由編譯器自動分配釋放,存放基本數(shù)據(jù)類型、局部變量和函數(shù)參數(shù),其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧苦掘。

2.堆(heap) 由程序員來分配和管理內(nèi)存,存放類類型(對象等).注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事换帜,分配方式類似于鏈表。

3.全局區(qū)(靜態(tài)區(qū) static) 存儲全局變量和靜態(tài)變量.初始化的全局變量和靜態(tài)變量在一塊區(qū)域鹤啡,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域(BSS)惯驼。 - 程序結(jié)束后由系統(tǒng)釋放

4.文字常量區(qū) 常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放

5.方法區(qū)(程序代碼區(qū)) 存放函數(shù)體的二進制代碼.

創(chuàng)建局部block時,系統(tǒng)默認(rèn)把其創(chuàng)建在棧,出了當(dāng)前block方法體就會被自動釋放.Block的copy递瑰、retain祟牲、release操作 不同于NSObject的copy、retain抖部、release操作,只要實現(xiàn)一個對周圍變量沒有引用的Block说贝,就會顯示為是NSGlobalBlock.如果其中加入了對局部變量的引用,就是NSStackBlock如果你對一個NSStackBlock對象使用了Block_copy()或者發(fā)送了copy消息慎颗,就會得到NSMallocBlock.

當(dāng)block類型是NSGlobalBlock使用copy retain release 都是無效的 如果沒有使用外部變量默認(rèn)是NSGlobalBlock

當(dāng)block引用外部變量時 會轉(zhuǎn)化類型成NSStackBlock 對copy操作有效果:會把block類型改變成NSMallocBlock copy后的block放在堆上面 對copy 和retain release 操作有效

注意NSMallocBlock類型的Block 雖然使用copy retain release 有效 但是系統(tǒng)顯示的引用計數(shù)一直1 盡量不要對block使用retain

Block_copy(<#...#>)和copy效果一樣

Block_release(<#...#>)和release效果一樣

總結(jié)如下:

1)NSGlobalBlock:retain乡恕、copy、release操作都無效俯萎;
2)NSStackBlock:retain几颜、release操作無效,必須注意的是讯屈,NSStackBlock在函數(shù)返回后蛋哭,Block內(nèi)存將被回收。即使retain也沒用涮母。容易犯的錯誤是[mutableAarry addObject:stackBlock]谆趾,(補:在ARC中不用擔(dān)心此問題,因為ARC中會默認(rèn)將實例化的Block拷貝到堆上)在函數(shù)出棧后叛本,從mutableAarry中取到的stackBlock已經(jīng)被回收沪蓬,變成了野指針。正確的做法是先將[stackBlock copy]到堆上来候,然后加入數(shù)組:[mutableAarry addObject:[[stackBlock copy] autorelease]]跷叉。支持copy,copy之后生成新的NSMallocBlock類型對象营搅。
3)NSMallocBlock支持retain云挟、release,雖然retainCount始終是1转质,但內(nèi)存管理器中仍然會增加园欣、減少計數(shù)。copy之后不會生成新的對象休蟹,只是增加了一次引用沸枯,類似retain日矫;
4)Block_copy與copy等效,Block_release與release等效绑榴;
5)對Block不管是retain哪轿、copy、release都不會改變引用計數(shù)retainCount翔怎,retainCount始終是1窃诉;
6)盡量不要對Block使用retain操作,不方便管理。


接下來看例子

1姓惑、局部變量褐奴,在Block中只讀。

Block定義時copy變量的值于毙,在Block中作為常量使用敦冬,所以即使變量的值在Block外改變,也不影響它在Block中的值
int a =3;
    void(^myblock)()=^{
        NSLog(@">>>%d",a);
    };
    a=5;
    myblock();//輸出3

2唯沮、在block內(nèi)修改外部聲明的局部變量,那么一定要對該變量加__block標(biāo)記.

block內(nèi)部使用外部局部變量的時候 會自動把變量copy一份 內(nèi)部會使用copy后的變量 如果想使用原變量 (不使外部變量copy) 需要在局部變量前面加上__block
__block int b =2;
    void(^myblock1)()=^{
        b +=1;
        NSLog(@"----->>>>%d",b);
    };
    b =10000;
    myblock1();//輸出10001

3脖旱、Static修飾的或全局變量,當(dāng)在外界發(fā)生改變時,block內(nèi)部也會隨之發(fā)生改變

因為全局變量或靜態(tài)變量在內(nèi)存中的地址是固定的,Block在讀取該變量值的時候是直接從其所在內(nèi)存讀出介蛉,獲取到的是最新值萌庆,而不是在定義時copy的常量.在Block內(nèi)部修改外部全局變量時 不用加__block;在Block內(nèi)部 修改 static 描述的變量 不用加__block。
Block變量币旧,被__Block修飾的變量稱作Block變量践险。 基本類型的Block變量等效于全局變量或靜態(tài)變量 但對象的block變量不會
//當(dāng)全局變量在block內(nèi)部使用,block內(nèi)部不會Copy全局變量,而是使用原值.因為全局變量前面隱藏__block
    self.d=7777;
    void(^myblock2)() = ^{
        NSLog(@"%d",self.d);
    };
    self.d=8888;
    myblock2();//8888
    
    //static 描述的變量 block內(nèi)部不會copy 而是使用原值
    static int w =30000;
    void(^myblock3)() = ^{
        NSLog(@"%d",w);
    };
    w=50000;
    myblock3 ();//50000

4、為什么在block里面使用self會導(dǎo)致強引用而無法釋放內(nèi)存呢?這里我們用一個例子來說明一下:

block強引用的原因:
[self.teacher requestData:^(NSData *data) {
    self.name = @"case";
}];

在這里吹菱,self強引用了teacher, teacher又強引用了一個block巍虫,而該block在回調(diào)時又調(diào)用了self,會導(dǎo)致該block又強引用了self鳍刷,造成了一個保留環(huán)占遥,最終導(dǎo)致self無法釋放。

解決方案:
__weak typeof(self) weakSelf = self;
    [self.teacher requestData:^(NSData *data) {
        typeof(weakSelf) strongSelf = weakSelf;
       strongSelf.name = @"case";
    }];

通過__weak的修飾输瓜,先把self弱引用(默認(rèn)是強引用瓦胎,實際上self是有個隱藏的__strong修飾的),然后在block回調(diào)里用weakSelf尤揣,這樣就會打破保留環(huán)搔啊,從而避免了循環(huán)引用,如下:self -> teacher -> block -> weakSelf
PS:一般會在block回調(diào)里再強引用一下weakSelf(typeof(weakSelf) strongSelf = weakSelf;)芹缔,因為__weak修飾的都是存在棧內(nèi)坯癣,可能隨時會被系統(tǒng)釋放,造成后面調(diào)用weakSelf時weakSelf可能已經(jīng)是nil了最欠,后面用weakSelf調(diào)用任何代碼都是無效的示罗。

這里要說一下,除了用__weak這種方式芝硬,還有其他兩種方式可以打破block的循環(huán)引用蚜点,分別是重新聲明一個用__block修飾的控制器的臨時變量,然后在block里面使用的當(dāng)前控制器用這個臨時變量替代拌阴,假如說當(dāng)前控制器的名稱是PushViewController绍绘,那么

__block PushViewController *vc = self;
然后在block用vc代替self,并在不再使用self的時候迟赃,將vc指向nil(將堆區(qū)的vc置空)陪拘。
其原理是__block 本質(zhì)是拷貝當(dāng)前被block捕捉到的對象到block結(jié)構(gòu)體,另一個內(nèi)存區(qū)域,生產(chǎn)新的空間變量,當(dāng)在block中使用了被聲明的局部變量以后,也就改變了局部變量的內(nèi)容地址(由棧區(qū)拷貝到了堆區(qū),棧區(qū)的block也就變成了堆區(qū)的block)
不用__block 是值傳遞纤壁,用__block  是指針傳遞

還有一種是將當(dāng)前控制器對象當(dāng)做參數(shù)傳入到block中進行使用(也就是把當(dāng)前的控制器當(dāng)做臨時變量當(dāng)做參數(shù)傳入block)

//block實現(xiàn)
self.block = ^(PushViewController *vc){}
//block的調(diào)用
self.block(self);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末左刽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酌媒,更是在濱河造成了極大的恐慌欠痴,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秒咨,死亡現(xiàn)場離奇詭異喇辽,居然都是意外死亡,警方通過查閱死者的電腦和手機雨席,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門菩咨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陡厘,你說我怎么就攤上這事抽米。” “怎么了雏亚?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵缨硝,是天一觀的道長。 經(jīng)常有香客問我罢低,道長查辩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任网持,我火速辦了婚禮宜岛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘功舀。我一直安慰自己萍倡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布辟汰。 她就那樣靜靜地躺著列敲,像睡著了一般阱佛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戴而,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天凑术,我揣著相機與錄音,去河邊找鬼所意。 笑死淮逊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扶踊。 我是一名探鬼主播泄鹏,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼秧耗!你這毒婦竟也來了备籽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤绣版,失蹤者是張志新(化名)和其女友劉穎胶台,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杂抽,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡诈唬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缩麸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铸磅。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杭朱,靈堂內(nèi)的尸體忽然破棺而出阅仔,到底是詐尸還是另有隱情,我是刑警寧澤弧械,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布八酒,位于F島的核電站,受9級特大地震影響刃唐,放射性物質(zhì)發(fā)生泄漏羞迷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一画饥、第九天 我趴在偏房一處隱蔽的房頂上張望衔瓮。 院中可真熱鬧,春花似錦抖甘、人聲如沸热鞍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薇宠。三九已至偷办,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昼接,已是汗流浹背爽篷。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工悴晰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慢睡,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓铡溪,卻偏偏與公主長得像漂辐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子棕硫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361