1 block的基本概念
1.1 block的產(chǎn)生和用途
代碼塊Block是蘋(píng)果在iOS4開(kāi)始引入的對(duì)C語(yǔ)言的擴(kuò)展,用來(lái)實(shí)現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類型,其可以正常定義變量、作為參數(shù)、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時(shí)候調(diào)用,目前Block已經(jīng)廣泛應(yīng)用于iOS開(kāi)發(fā)中。Block實(shí)際上是OC語(yǔ)言對(duì)閉包的實(shí)現(xiàn),是帶有自動(dòng)變量值的匿名函數(shù)。Block既可以定義在函數(shù)內(nèi)部也可以定義在函數(shù)外部。只有在調(diào)用block的時(shí)候,block{}內(nèi)的代碼才會(huì)執(zhí)行。
1.2 閉包的概念
閉包就是一個(gè)函數(shù),或者一個(gè)指向函數(shù)的指針,加上這個(gè)函數(shù)執(zhí)行的非局部變量。閉包允許一個(gè)函數(shù)訪問(wèn)聲明該函數(shù)運(yùn)行上下文中的變量,甚至可以訪問(wèn)不同運(yùn)行上文中的變量。
1.3 block的聲明與定義
int (^myBlock)(int) = ^(int num){
return num*num;
};
int 代表返回類型 ^代表的這是一個(gè)block
myBlock代表block的名字 (int)代表參數(shù)類型
(int num)代表形參的類型及名字
在定義block的時(shí)候一定不要忘記在最后加上分號(hào)
block是一個(gè)對(duì)象,內(nèi)部是一個(gè)結(jié)構(gòu)體,在創(chuàng)建block 的時(shí)候就把它的指針傳給了block,所以可以直接調(diào)用block塊
但是為了使用block方便,一般在定義block的時(shí)候會(huì)用typedef簡(jiǎn)化block如:typedef void(^myBlock)(int) ;
1.4 block的實(shí)質(zhì)
通俗點(diǎn)說(shuō),block是一個(gè)結(jié)構(gòu)體,它里面包含了函數(shù)指針以及block外部上下文變量等信息,這個(gè)函數(shù)指針指向的是在定義block時(shí)的代碼塊。
1.5 block最常使用的情況
block最常使用的方式:界面?zhèn)髦担换卣{(diào)
2 Block對(duì)變量的訪問(wèn)與修改
2.1 block對(duì)局部變量的訪問(wèn)與修改
(1) block1 = ^{
NSLog(@"哈哈哈哈哈哈");
};
block1();
(2) NSInteger aaa = 100;
block2 = ^{
aaa++; //會(huì)報(bào)錯(cuò)
NSLog(@"aaa的值為%ld",aaa);
};
(3)__block NSInteger aaa = 100;
block2 = ^{
aaa++;
NSLog(@"aaa的值為%ld",aaa); //aaa = 101
};
(4) NSInteger aaa = 100;
block5 = ^{
NSLog(@"aaa的值為%ld",aaa);//aaa = 100
};
aaa = 105;
block5();
(5)__block NSInteger ddd = 10;
block7 = ^{
ddd = ddd +1;
NSLog(@"ddd現(xiàn)在的值為%ld",ddd);
};
ddd = 100;
block7();
通過(guò)(1)說(shuō)明block只有在調(diào)用的時(shí)候其內(nèi)部代碼才會(huì)執(zhí)行,不調(diào)用其內(nèi)部代碼不會(huì)執(zhí)行,通過(guò)(2)和(3)說(shuō)明block可以訪問(wèn)局部變量,但是不能對(duì)其進(jìn)行修改,如果要在block里面修改局部變量的值,那么要在局部變量前面加_ block修飾符 。通過(guò)(4)和(5)可以看出如果局部變量不加 block來(lái)進(jìn)行修飾,那么當(dāng)在block定義后block調(diào)用前修改局部變量的值,那么block里面的局部變量依然是block定義時(shí)的值(因?yàn)樵赽lock定義的時(shí)候就將局部變量的值傳給了block所指向的結(jié)構(gòu)體,所以局部變量的改變不會(huì)影響block里面的局部變量,但是block里面的局部變量也是不可修改的),如果局部變量前面加 _block修飾,那么當(dāng)在block定以后block執(zhí)行前對(duì)局部變量進(jìn)行修改,block里面的局部變量值也會(huì)進(jìn)行相應(yīng)的改變(因?yàn)橛胈_block聲明局部變量后,在block里面是把局部變量的指針傳給block指向的結(jié)構(gòu)體的,所以其值可以被改變)。
2.2block對(duì)全局變量的訪問(wèn)
(1)// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};// 調(diào)用后控制臺(tái)輸出"global = 100"
myBlock();
(2)// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);// 調(diào)用后控制臺(tái)輸出"global = 101"
};
global = 101;
myBlock();
(3)// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);// 調(diào)用后控制臺(tái)輸出"global = 101"
};
myBlock();
結(jié)論:block可以直接對(duì)全局變量進(jìn)行訪問(wèn)和修改(全局變量所占用的內(nèi)存只有一份,供所有函數(shù)共同調(diào)用,在Block定義時(shí)并未將全局變量的值或者指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對(duì)局部變量進(jìn)行修改會(huì)影響B(tài)lock內(nèi)部的值,同時(shí)內(nèi)部的值也是可以修改的)
2.3 block對(duì)全局靜態(tài)變量的訪問(wèn)
(1)// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);// 調(diào)用后控制臺(tái)輸出"global = 100"
};
myBlock();
(2)// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global= %d", global);// 調(diào)用后控制臺(tái)輸出"global = 101"
};
global = 101;
myBlock();
(3)// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);// 調(diào)用后控制臺(tái)輸出"global = 101"
};
myBlock();
結(jié)論:block里面可以直接訪問(wèn)和修改靜態(tài)全局變量(因?yàn)樵贐lock定義時(shí)便是將靜態(tài)變量的指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對(duì)靜態(tài)變量進(jìn)行修改會(huì)影響B(tài)lock內(nèi)部的值,同時(shí)內(nèi)部的值也是可以修改的)
3 block的使用方式
block的使用與代理很類似,代理是讓某個(gè)類滿足自己的某個(gè)協(xié)議,然后實(shí)現(xiàn)協(xié)議里面的方法。Block是自己在本類定義了一個(gè)代碼塊,但是在需要的時(shí)候才會(huì)去調(diào)用。下面是一個(gè)block使用的小列子:
業(yè)務(wù)需求是在一個(gè)封裝的view中點(diǎn)擊了tableView的某一行,然后通過(guò)block把這一行上的字符串傳出去進(jìn)行處理,下面是具體實(shí)現(xiàn):
view的.h文件:
typedefvoid (^ChooseViewClick)(NSString*);
@interfaceChooseView : UIView<UITableViewDataSource,UITableViewDelegate>
@property(nonatomic,copy)ChooseViewClick chooseClick;
@end
當(dāng)tableView的某一行被點(diǎn)擊時(shí):
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismiss];
NSString *str =_showArray[indexPath.row];
if(self.chooseClick) {
self.chooseClick(str);
}
}
在另一個(gè)控制器進(jìn)行處理
_chooseView.chooseClick = ^(NSString *str)
{
_selectTag = 0;
[btnsetTitle:str forState:UIControlStateNormal];
};
4 block使用之避免循環(huán)引用
在使用block的時(shí)候要特別注意的是避免循環(huán)引用,循環(huán)引用指的是兩個(gè)對(duì)象都相互強(qiáng)引用了對(duì)方,從而導(dǎo)致誰(shuí)也釋放不了,引起內(nèi)存泄露問(wèn)題。最常見(jiàn)的分為兩種情況,一種是這個(gè)對(duì)象擁有這個(gè)block,但是在block塊里面又引用了這個(gè)對(duì)象本身,第二種是block是宿主的一個(gè)屬性,但是在block里面又訪問(wèn)了宿主的其他屬性。
第一種情況,block是self本身的一個(gè)屬性,在block里面又訪問(wèn)了宿主本身:
self.myblock = ^{
[self doSomething];
};
在這里會(huì)引起循環(huán)引用,解決辦法是用__weak修飾,正確代碼如下:
__weak typedef(self) weakSelf = self;
self.myblock = ^{
[weakSelf doSomething];
};
第二種情況,block是宿主本身的屬性,在block里面又訪問(wèn)了宿主的其他屬性:
self.myblock = ^{
[self.muArray removeAllObjects];
}; //引起循環(huán)引用
此時(shí)解決方案跟上面一樣,可以加__block 或者_(dá)_weak 來(lái)修飾,正確代碼如下:
__block typedef(self) weakSelf = self;
self.myblock = ^{
[weakSelf.muArray removeAllObject];
};