日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Block总结

發(fā)布時間:2024/7/23 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Block总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、簡介 Block代碼塊,本質(zhì)上它和其它變量類似,不同的是代碼塊傳遞的是函數(shù)體,類似于@selector的功能。調(diào)用和其它標(biāo)準(zhǔn)函數(shù)一樣。聲明方式有差別。
二、代碼塊定義 例:int ?( ^ ? ?MyBlock)( int ) = ^ ?(int m){ return m * 3; }; ? ? ? ? 1 ? ?2 ? ? ? ? 3 ? ? ? ? 4 ? ? ? 5 ? ? ? ?6 ? ? ? ? ?7 含義: 1:返回類型 2:^是代碼塊的語法標(biāo)記 3:代碼塊的變量名 4:參數(shù)類型 5:定義代碼塊對象 6:參數(shù)名是m 7:代碼塊對象的主體部分
三、代碼塊調(diào)用 拿上面的為例:int newValue = MyBlock(3);
四、代碼塊語法的一些規(guī)則: (1)當(dāng)在block中直接使用局部變量時,局部變量會被當(dāng)做是常量編碼到block中(兩個變量),所以不能在Block中直接修改局部變量 (2)代碼塊如果想要遞歸調(diào)用,代碼塊變量必須為全局變量或者靜態(tài)變量。 (3)在代碼塊中可以使用和改變?nèi)肿兞亢挽o態(tài)變量。 (4)代碼塊可以使用局部變量,但是要改變值的話,要在局部變量前面加關(guān)鍵字__block。
五、Block存儲域 根據(jù)Block中是否引用了外部變量,可以將Block存儲區(qū)域分為三種:NSGlobalBlock、NSStackBlock、NSMallocBlock。 1.NSGlobalBlock-存儲在全局?jǐn)?shù)據(jù)區(qū)域: 對于沒有引用外部變量的Block,無論在ARC還是非ARC下,類型都是__NSGlobalBlock__,這種類型的block可以理解成一種全局的block,和全局變量一樣。同時,對他進(jìn)行Copy或者Retain操作也是無效的。 2.NSStackBlock-存儲在棧上:而對于引用了外部變量的block,如果沒有對他進(jìn)行copy,他的作用域只會在聲明他的函數(shù)棧內(nèi)(類型是__NSStackBlock__)。對其執(zhí)行retain操作沒有作用。 3.NSMallocBlock-存儲在堆上:對NSStackBlock類型的block,執(zhí)行copy操作,block會被復(fù)制到堆上。retain和copy對會使其引用計數(shù)加1。
說明:一般來說出問題的Block大部分都是NSStackBlock,超過了NSStackBlock的作用域NSStackBlock就會銷毀。
六、什么時候要對NSConcreteStackBlock執(zhí)行copy操作? 1.場景:配置在棧上的Block,如果其所屬的變量作用域結(jié)束該Block就會廢棄。這個時候如果繼續(xù)使用該Block,就應(yīng)該使用copy方法,將NSConcreteStackBlock拷貝為_NSConcreteMallocBlock。當(dāng)_NSConcreteMallocBlock的引用計數(shù)變?yōu)?,該_NSConcreteMallocBlock就會被釋放。 2.如果是非ARC環(huán)境,需要顯式的執(zhí)行copy或者antorelease方法。 3.而當(dāng)ARC有效的時候,實際上大部分情況下編譯器已經(jīng)為我們做好了,自動的將Block從棧上復(fù)制到堆上。包括以下幾個情況: (1)Block作為返回值時 類似在非ARC的時候,對返回值Block執(zhí)行[[returnedBlock copy] autorelease]; (2)方法的參數(shù)中傳遞Block時 (3)Cocoa框架中方法名中還有useringBlock等時 (4)GCD相關(guān)的一系列API傳遞Block時。 比如:[mutableAarry addObject:stackBlock];這段代碼在非ARC環(huán)境下肯定有問題,而在ARC環(huán)境下方法參數(shù)中傳遞NSConcreteStackBlock會自動執(zhí)行copy。
七、一般的應(yīng)用場景: 假設(shè)A要調(diào)用B完成一件事,但是在B完成事情之后要通知A一下,這時候可以使用Block。 1.首先在B中定義一個Block類型,比如: ? typedef void (^DoSomeThingFinished)(id parame); 2.定義Block實例變量,DoSomeThingFinished aDoSomeThingFinished; 3.定義B的動作方法:-(void)doSomeThing:(DoSomeThingFinished)doSomeThingFinished; 4.動作方法實現(xiàn)規(guī)則: (1)當(dāng)doSomeThing方法被調(diào)用時,首先將doSomeThingFinished要copy一下(block將從棧賦值到堆上),并且賦值給aDoSomeThingFinished,以防止調(diào)用block時,block已經(jīng)銷毀。 (2)當(dāng)事情完成時,首先檢查代碼塊變量aDoSomeThingFinished是否為nil,如果不為nil,調(diào)用aDoSomeThingFinished代碼塊變量,并傳入合適的值。然后release代碼塊變量aDoSomeThingFinished,并賦值nil。 5.在B銷毀時,檢查aDoSomeThingFinished是否為nil,如果不為nil,release,并且賦值nil。 6.A調(diào)用B的方法,如下: [b doSomeThing:^(id parame){ /*動作完成時要做的事情*/ }];
八、Block循環(huán)引用: (1)場景:假如A調(diào)用B,B的API使用了Block。在A中有一個B的實例作為成員變量,此時A引用了B;A在使用B的API的時候,在Block代碼中使用了self關(guān)鍵字或者A的成員變量,導(dǎo)致block引用了A,即B引用了A。從而導(dǎo)致循環(huán)引用。 (2)導(dǎo)致的問題:如果Block被很好的執(zhí)行,并且B release了Block,A的引用計數(shù)自然就降下來了,循環(huán)引用消失。但是,如果B長時間或者根本沒有調(diào)用Block,導(dǎo)致B一直引用Block并且沒有釋放它,從而A的引用計數(shù)一直降不下來,導(dǎo)致A不能釋放。 (3)解決辦法: MRC:重新定義一下self,如下:__block typeof(self) bself = self; 將self定義為__block類型,在block中使用bself變量,此時block就不會retain當(dāng)前控制器了。當(dāng)A銷毀前,首先將B銷毀掉,B銷毀時,代碼塊被release并置nil,block將不會被執(zhí)行。__block關(guān)鍵字告訴編譯器,不要retain該變量。 ARC:對于ARC下, 為了防止循環(huán)引用, 我們使用__weak來修飾在Block中使用的對象。 (4)說明:在使用Block時,可以大膽的說,百分之九十九的情況下是不需要使用weakSelf的。 Block代碼塊引用self以至retain當(dāng)前控制器,是有道理的,這樣做是為了讓代碼塊得到很好的執(zhí)行,如果當(dāng)前控制器已經(jīng)釋放了,在回調(diào)代碼塊的時候,就不正常了。 我們需要做的是,維護(hù)好block的調(diào)用關(guān)系以及生命周期就可以了,讓block及時釋放,對當(dāng)前控制器的引用自然而然也就釋放了。 那剩下的那百分之一是什么情況呢?即對Block的持有者一直保持Block不釋放的情況,比如以Block的方式使用加速計,除非將加速計停止,否則Block一直會被持有,如果不希望這樣,可以在Block中使用weakSelf。
九、Block內(nèi)存演示代碼:

//======================================================

//ARC:正確

//MRC:正確


void exampleA() {

? ? char a = 'A';

? ? ^{

? ? ? ? printf("%c\n", a);

? ? }();

}


//======================================================

//ARC:正確

//MRC:正確---EXC_BAD_ACCESS

//說明:添加的block在棧上,ARC下,block會被copyMRC下,如果沒有執(zhí)行copy操作,此block在函數(shù)體結(jié)束之后就釋放了。

//addObject方法執(zhí)行的是retain操作,不起作用。


void exampleB() {

? ? NSMutableArray *array = [NSMutableArray array];

? ? exampleB_addBlockToArray(array);

? ? void (^block)() = [array objectAtIndex:0];

? ? block();

}


void exampleB_addBlockToArray(NSMutableArray *array) {

? ? char b = 'B';

? ? [array addObject:^{

? ? ? ? printf("%c\n", b);

? ? }];

}


//======================================================

//ARC:正確

//MRC:正確

//說明:添加的Block為全局Block,函數(shù)體結(jié)束之后,它還存在。


void exampleC() {

? ? NSMutableArray *array = [NSMutableArray array];

? ? exampleC_addBlockToArray(array);

? ? void (^block)() = [array objectAtIndex:0];

? ? block();

}


void exampleC_addBlockToArray(NSMutableArray *array) {

? ? [array addObject:^{

? ? ? ? printf("C\n");

? ? }];

}


//======================================================

//ARC:正確

//MRC:正確,編譯不通過--Returning block that lives on the local stack

//說明:添加的Block在棧上,ARC下,block會被copyMRC下,如果沒有執(zhí)行copy操作,此block在函數(shù)體結(jié)束之后就釋放了。


typedef void (^dBlock)();


dBlock exampleD_getBlock() {

? ? char d = 'D';

? ? return ^{

? ? ? ? printf("%c\n", d);

? ? };

}


void exampleD() {

? ? exampleD_getBlock()();

}


//=======================================================

//ARC:正確

//MRC:不正確。這個例子和例子4類似,除了編譯器沒有認(rèn)出有錯誤,所以代碼會進(jìn)行編譯然后崩潰。更糟糕的是,這個例子比較特別,如果你關(guān)閉了優(yōu)化,則可以正常運行。所以在測試的時候需要注意。


typedef void (^eBlock)();


eBlock exampleE_getBlock() {

? ? char e = 'E';

? ? void (^block)() = ^{

? ? ? ? printf("%c\n", e);

? ? };

? ? return block;

}


void exampleE() {

? ? eBlock block = exampleE_getBlock();

? ? block();

}

總結(jié)

以上是生活随笔為你收集整理的Block总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。