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