Block总结
二、代碼塊定義 例:int ?( ^ ? ?MyBlock)( int ) = ^ ?(int m){ return m * 3; }; ? ? ? ? 1 ? ?2 ? ? ? ? 3 ? ? ? ? 4 ? ? ? 5 ? ? ? ?6 ? ? ? ? ?7 含義: 1:返回類型 2:^是代碼塊的語(yǔ)法標(biāo)記 3:代碼塊的變量名 4:參數(shù)類型 5:定義代碼塊對(duì)象 6:參數(shù)名是m 7:代碼塊對(duì)象的主體部分
三、代碼塊調(diào)用 拿上面的為例:int newValue = MyBlock(3);
四、代碼塊語(yǔ)法的一些規(guī)則: (1)當(dāng)在block中直接使用局部變量時(shí),局部變量會(huì)被當(dāng)做是常量編碼到block中(兩個(gè)變量),所以不能在Block中直接修改局部變量 (2)代碼塊如果想要遞歸調(diào)用,代碼塊變量必須為全局變量或者靜態(tài)變量。 (3)在代碼塊中可以使用和改變?nèi)肿兞亢挽o態(tài)變量。 (4)代碼塊可以使用局部變量,但是要改變值的話,要在局部變量前面加關(guān)鍵字__block。
五、Block存儲(chǔ)域 根據(jù)Block中是否引用了外部變量,可以將Block存儲(chǔ)區(qū)域分為三種:NSGlobalBlock、NSStackBlock、NSMallocBlock。 1.NSGlobalBlock-存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū)域: 對(duì)于沒(méi)有引用外部變量的Block,無(wú)論在ARC還是非ARC下,類型都是__NSGlobalBlock__,這種類型的block可以理解成一種全局的block,和全局變量一樣。同時(shí),對(duì)他進(jìn)行Copy或者Retain操作也是無(wú)效的。 2.NSStackBlock-存儲(chǔ)在棧上:而對(duì)于引用了外部變量的block,如果沒(méi)有對(duì)他進(jìn)行copy,他的作用域只會(huì)在聲明他的函數(shù)棧內(nèi)(類型是__NSStackBlock__)。對(duì)其執(zhí)行retain操作沒(méi)有作用。 3.NSMallocBlock-存儲(chǔ)在堆上:對(duì)NSStackBlock類型的block,執(zhí)行copy操作,block會(huì)被復(fù)制到堆上。retain和copy對(duì)會(huì)使其引用計(jì)數(shù)加1。
說(shuō)明:一般來(lái)說(shuō)出問(wèn)題的Block大部分都是NSStackBlock,超過(guò)了NSStackBlock的作用域NSStackBlock就會(huì)銷毀。
六、什么時(shí)候要對(duì)NSConcreteStackBlock執(zhí)行copy操作? 1.場(chǎng)景:配置在棧上的Block,如果其所屬的變量作用域結(jié)束該Block就會(huì)廢棄。這個(gè)時(shí)候如果繼續(xù)使用該Block,就應(yīng)該使用copy方法,將NSConcreteStackBlock拷貝為_(kāi)NSConcreteMallocBlock。當(dāng)_NSConcreteMallocBlock的引用計(jì)數(shù)變?yōu)?,該_NSConcreteMallocBlock就會(huì)被釋放。 2.如果是非ARC環(huán)境,需要顯式的執(zhí)行copy或者antorelease方法。 3.而當(dāng)ARC有效的時(shí)候,實(shí)際上大部分情況下編譯器已經(jīng)為我們做好了,自動(dòng)的將Block從棧上復(fù)制到堆上。包括以下幾個(gè)情況: (1)Block作為返回值時(shí) 類似在非ARC的時(shí)候,對(duì)返回值Block執(zhí)行[[returnedBlock copy] autorelease]; (2)方法的參數(shù)中傳遞Block時(shí) (3)Cocoa框架中方法名中還有useringBlock等時(shí) (4)GCD相關(guān)的一系列API傳遞Block時(shí)。 比如:[mutableAarry addObject:stackBlock];這段代碼在非ARC環(huán)境下肯定有問(wèn)題,而在ARC環(huán)境下方法參數(shù)中傳遞NSConcreteStackBlock會(huì)自動(dòng)執(zhí)行copy。
七、一般的應(yīng)用場(chǎng)景: 假設(shè)A要調(diào)用B完成一件事,但是在B完成事情之后要通知A一下,這時(shí)候可以使用Block。 1.首先在B中定義一個(gè)Block類型,比如: ? typedef void (^DoSomeThingFinished)(id parame); 2.定義Block實(shí)例變量,DoSomeThingFinished aDoSomeThingFinished; 3.定義B的動(dòng)作方法:-(void)doSomeThing:(DoSomeThingFinished)doSomeThingFinished; 4.動(dòng)作方法實(shí)現(xiàn)規(guī)則: (1)當(dāng)doSomeThing方法被調(diào)用時(shí),首先將doSomeThingFinished要copy一下(block將從棧賦值到堆上),并且賦值給aDoSomeThingFinished,以防止調(diào)用block時(shí),block已經(jīng)銷毀。 (2)當(dāng)事情完成時(shí),首先檢查代碼塊變量aDoSomeThingFinished是否為nil,如果不為nil,調(diào)用aDoSomeThingFinished代碼塊變量,并傳入合適的值。然后release代碼塊變量aDoSomeThingFinished,并賦值nil。 5.在B銷毀時(shí),檢查aDoSomeThingFinished是否為nil,如果不為nil,release,并且賦值nil。 6.A調(diào)用B的方法,如下: [b doSomeThing:^(id parame){ /*動(dòng)作完成時(shí)要做的事情*/ }];
八、Block循環(huán)引用: (1)場(chǎng)景:假如A調(diào)用B,B的API使用了Block。在A中有一個(gè)B的實(shí)例作為成員變量,此時(shí)A引用了B;A在使用B的API的時(shí)候,在Block代碼中使用了self關(guān)鍵字或者A的成員變量,導(dǎo)致block引用了A,即B引用了A。從而導(dǎo)致循環(huán)引用。 (2)導(dǎo)致的問(wèn)題:如果Block被很好的執(zhí)行,并且B release了Block,A的引用計(jì)數(shù)自然就降下來(lái)了,循環(huán)引用消失。但是,如果B長(zhǎng)時(shí)間或者根本沒(méi)有調(diào)用Block,導(dǎo)致B一直引用Block并且沒(méi)有釋放它,從而A的引用計(jì)數(shù)一直降不下來(lái),導(dǎo)致A不能釋放。 (3)解決辦法: MRC:重新定義一下self,如下:__block typeof(self) bself = self; 將self定義為_(kāi)_block類型,在block中使用bself變量,此時(shí)block就不會(huì)retain當(dāng)前控制器了。當(dāng)A銷毀前,首先將B銷毀掉,B銷毀時(shí),代碼塊被release并置nil,block將不會(huì)被執(zhí)行。__block關(guān)鍵字告訴編譯器,不要retain該變量。 ARC:對(duì)于ARC下, 為了防止循環(huán)引用, 我們使用__weak來(lái)修飾在Block中使用的對(duì)象。 (4)說(shuō)明:在使用Block時(shí),可以大膽的說(shuō),百分之九十九的情況下是不需要使用weakSelf的。 Block代碼塊引用self以至retain當(dāng)前控制器,是有道理的,這樣做是為了讓代碼塊得到很好的執(zhí)行,如果當(dāng)前控制器已經(jīng)釋放了,在回調(diào)代碼塊的時(shí)候,就不正常了。 我們需要做的是,維護(hù)好block的調(diào)用關(guān)系以及生命周期就可以了,讓block及時(shí)釋放,對(duì)當(dāng)前控制器的引用自然而然也就釋放了。 那剩下的那百分之一是什么情況呢?即對(duì)Block的持有者一直保持Block不釋放的情況,比如以Block的方式使用加速計(jì),除非將加速計(jì)停止,否則Block一直會(huì)被持有,如果不希望這樣,可以在Block中使用weakSelf。
九、Block內(nèi)存演示代碼:
//======================================================
//ARC:正確
//MRC:正確
void exampleA() {
? ? char a = 'A';
? ? ^{
? ? ? ? printf("%c\n", a);
? ? }();
}
//======================================================
//ARC:正確。
//MRC:不正確---EXC_BAD_ACCESS
//說(shuō)明:添加的block在棧上,ARC下,block會(huì)被copy。MRC下,如果沒(méi)有執(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:正確
//說(shuō)明:添加的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:不正確,編譯不通過(guò)--Returning block that lives on the local stack
//說(shuō)明:添加的Block在棧上,ARC下,block會(huì)被copy。MRC下,如果沒(méi)有執(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:不正確。這個(gè)例子和例子4類似,除了編譯器沒(méi)有認(rèn)出有錯(cuò)誤,所以代碼會(huì)進(jìn)行編譯然后崩潰。更糟糕的是,這個(gè)例子比較特別,如果你關(guān)閉了優(yōu)化,則可以正常運(yùn)行。所以在測(cè)試的時(shí)候需要注意。
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é)
- 上一篇: 计算机PPT03,南京大学计算机网络课件
- 下一篇: MVPVM模式介绍