Block
block其實(shí)就是一個(gè)代碼塊,把你想要執(zhí)行的代碼封裝在這個(gè)代碼塊里,等到需要的時(shí)候再去調(diào)用。那block是OC對(duì)象嗎?答案是肯定的
1、為什么block中要使用__weak來(lái)修飾self?
循環(huán)引用,block在iOS中被視為對(duì)象,因此它的生命周期會(huì)等到它的持有者生命周期結(jié)束才會(huì)結(jié)束;另一方面,由于block捕獲變量的機(jī)制,self也會(huì)被block持有;導(dǎo)致循環(huán)引用。
2、Block的變量截獲
2.1局部變量截獲 是值截獲
//? ? __block NSInteger num = 3;
? ? NSInteger num = 3;
? ? NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
? ? ? ? returnn*num;
? ? };
? ? num = 1;
NSLog(@"%ld",(long)block(2));
//輸出6,加上__block,則輸出2
原因就是對(duì)局部變量num的截獲是值截獲。就是創(chuàng)建block的時(shí)候,已經(jīng)把a(bǔ)ge的值存儲(chǔ)在里面了
同樣,在block里如果修改變量num,也是無(wú)效的,甚至編譯器會(huì)報(bào)錯(cuò)。
2.2局部靜態(tài)變量截獲 是指針截獲,
static? NSInteger num = 3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
? ? ? ? return?n*num;
? ? };
? ? num = 1;
NSLog(@"%ld",(long)block(2));
輸出為2,意味著num = 1這里的修改num值是有效的,即是指針截獲。
同樣,在block里去修改變量m,也是有效的。
2.3可以看到局部變量被編譯成值形式,而靜態(tài)變量被編成指針形式,全局變量并未截獲。而__block修飾的變量也是以指針形式截獲的,并且生成了一個(gè)新的結(jié)構(gòu)體對(duì)象
Q:block對(duì)全局變量的捕獲方式是?
block不需要對(duì)全局變量捕獲,都是直接采用取值的
Q:為什么局部變量需要捕獲?
考慮作用域的問題,需要跨函數(shù)訪問,就需要捕獲
3、根據(jù)Block創(chuàng)建的位置不同,Block有三種類型,創(chuàng)建的Block對(duì)象分別會(huì)存儲(chǔ)到棧(NSStackBlock)、堆(NSMallocBlock)、全局?jǐn)?shù)據(jù)區(qū)域(NSGlobalBlock)
3.1 不使用外部變量的block是全局block,存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū)域
3.2使用了外部變量,但未進(jìn)行copy操作的block是棧block
3.3對(duì)棧block進(jìn)行copy操作,就是堆block,對(duì)全局block進(jìn)行copy操作還是全局block
即如果對(duì)棧Block進(jìn)行copy,將會(huì)copy到堆區(qū),對(duì)堆Block進(jìn)行copy,將會(huì)增加引用計(jì)數(shù),對(duì)全局Block進(jìn)行copy,因?yàn)槭且呀?jīng)初始化的,所以什么也不做。
4、block為什么用copy修飾
因?yàn)樵?b>MRC下,block在創(chuàng)建的時(shí)候,它的內(nèi)存是分配在棧(stack)上的,而不是在堆(heap)上,可能被隨時(shí)回收。他本身的作于域是屬于創(chuàng)建時(shí)候的作用域,一旦在創(chuàng)建時(shí)候的作用域外面調(diào)用block將導(dǎo)致程序崩潰。通過copy可以把block拷貝(copy)到堆,保證block的聲明域外使用。在ARC下寫不寫都行,編譯器會(huì)自動(dòng)對(duì)block進(jìn)行copy操作。
Block創(chuàng)建的時(shí)候它的內(nèi)存是分配在棧中的,它本身的作用域就是創(chuàng)建的時(shí)候的作用域,如果在此作用域外部調(diào)用block就會(huì)導(dǎo)致程序崩潰。因?yàn)闂^(qū)的特點(diǎn)就是創(chuàng)建的對(duì)象隨時(shí)可能被銷毀,一旦被銷毀后續(xù)調(diào)用這個(gè)空對(duì)象就會(huì)導(dǎo)致程序崩潰,而對(duì)block進(jìn)行copy,block的內(nèi)存就會(huì)分配到堆區(qū)。
使用retain也可以(有警告),但是block的retain行為默認(rèn)是用copy的行為實(shí)現(xiàn)的。
所以為了能夠在block的聲明域外使用,所以要把block拷貝(copy)到堆,所以說為了block屬性聲明和實(shí)際的操作一致,最好聲明為copy。
5、定義和使用
5.1、無(wú)參無(wú)返回
void (^ MyBlockOne)(void) = ^(void){
? ? NSLog(@"無(wú)參數(shù),無(wú)返回值");?
};?
MyBlockOne();//block的調(diào)用
5.2、有參無(wú)返回
void(^MyblockTwo)(int a) = ^(int a){
? ? NSLog(@"@ = %d我就是block,有參數(shù),無(wú)返回值",a);
};?
MyblockTwo(100);
5.3、有參有返回
int(^MyBlockThree)(int,int) = ^(int a,int b){? ?
? ? NSLog(@"%d我就是block,有參數(shù),有返回值",a + b);
? ? return a + b;?
};?
MyBlockThree(12,56);
5.4、無(wú)參有返回
int(^MyblockFour)(void) = ^{
? ? NSLog(@"無(wú)參數(shù),有返回值");
? ? return45;
? };
MyblockFour();
5.5、聲明
typedef void (^Block)();
typedef int (^MyBlock)(int , int);
typedef void(^ConfirmBlock)(BOOL isOK);
typedef void(^AlertBlock)(NSInteger alertTag);
定義屬性
@property (nonatomic,copy) MyBlock myBlockOne;
使用
self.myBlockOne = ^int (int ,int){
? ? //TODO
}
block在修改NSMutableArray,需不需要添加__block?
如修改NSMutableArray的存儲(chǔ)內(nèi)容的話,是不需要添加__block修飾的。
如修改NSMutableArray對(duì)象的本身,那必須添加__block修飾。
例子:
__blockNSMutableArray *arr = [[NSMutableArray alloc]init];//不加__block,下面改變可變數(shù)組arr的時(shí)候就報(bào)錯(cuò)
? ? [arr addObject:@"1"];
? ? void(^block)(void) = ^(){
//? ? ? ? [arr addObject:@"3"];
? ? ? ? NSArray *aaaa? = @[@"2"];
? ? ? ? arr = [NSMutableArray arrayWithArray:aaaa];
? ? ? ? NSLog(@"1_%@",arr);
? ? };
? ? NSLog(@"2_%@",arr);
? ? block();
? ? NSLog(@"3_%@",arr);