block定義
- 格式:
返回類型 (^block名字) (參數(shù)列表);
- 同C語言的定義函數(shù)指針一樣缘琅,C語言的函數(shù)指針定義格式:
返回類型 (*指針名字) (參數(shù)列表);
block賦值
名字 = ^{xxx};
- 如同普通的變量賦值一樣類型、分號都需要‘一一對應(yīng)’,如同
int a = 5;
蛤奥,^
標(biāo)志右邊的代碼段是block類型越庇。
block調(diào)用
block名字 (參數(shù)列表);
例(無參):
//定義
void (^valenti) ();
//賦值
valenti = ^{
NSLog(@"VaLenTi is MEEEEE!");
};
//調(diào)用
valenti();
例(有參):
//定義
void (^valenti) (NSString* name);
//賦值
valenti = ^(NSString* name){
NSLog(@"VaLenTi is %@!",name);
};
//調(diào)用
valenti(@"ME");
例(有參有返回值):
//定義
NSInteger (^sum)(NSInteger value1, NSInteger value2);
//賦值
sum = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
//調(diào)用
NSInteger result = sum(1,1);
typedef與block
如若有這樣的需求:定義四個block實現(xiàn)兩個參數(shù)的加減乘除捣染,他們的代碼是如下這樣的:
//加
NSInteger (^add)(NSInteger value1, NSInteger value2);
add = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
NSInteger result = add(1,1);
//減
NSInteger (^sub)(NSInteger value1, NSInteger value2);
sub = ^(NSInteger value1, NSInteger value2){
return value1 - value2;
};
NSInteger result2 = sub(1,1);
//乘
NSInteger (^mul)(NSInteger value1, NSInteger value2);
mul = ^(NSInteger value1, NSInteger value2){
return value1 * value2;
};
NSInteger result3 = mul(1,1);
//除
NSInteger (^div)(NSInteger value1, NSInteger value2);
div = ^(NSInteger value1, NSInteger value2){
return value1 / value2;
};
NSInteger result4 = div(1,1);
可見,除了block的名字和操作不同以外械馆,其余的結(jié)構(gòu)都是相同的,那么湿诊,相同的部分即可用typedef起別名的形式“抽取”出來狱杰。格式如下:
typedef 返回值類型 (^block名字) (參數(shù)列表)
- 同C語言的函數(shù)指針別名一樣,名稱即代表別名厅须。
上述代碼可改為:
//1.在類擴(kuò)展處定義別名
typedef NSInteger (^calculate) (NSInteger value1,NSInteger value2);
//2.在實現(xiàn)中定義對應(yīng)功能的block代碼并賦值
//加
calculate add = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
NSInteger result = add(1,1);
//減
calculate sub = ^(NSInteger value1, NSInteger value2){
return value1 - value2;
};
NSInteger result2 = sub(1,1);
//乘
calculate mul = ^(NSInteger value1, NSInteger value2){
return value1 * value2;
};
NSInteger result3 = mul(1,1);
//除
calculate div = ^(NSInteger value1, NSInteger value2){
return value1 / value2;
};
NSInteger result4 = div(1,1);
注意事項
- block可以訪問外部變量仿畸,例:
int a = 10;
void (^block)() = ^{
NSLog(@"%zd",a);
}
block中可以定義和外界同名的變量,在block內(nèi)部外部存在同名變量的情況下朗和,block訪問的變量是內(nèi)部變量-“就近原則”错沽。
默認(rèn)情況下,不可以在block內(nèi)部修改外部的變量眶拉,1中block內(nèi)部是不可以對a進(jìn)行賦值的千埃,因為block中的a和外部的a本質(zhì)上并不是同一個a,block訪問的外部變量會將外部的變量拷貝一份到堆內(nèi)存中忆植,驗證:
int a = 5;
NSLog(@"%p",&a);
void (^block)()= ^{
NSLog(@"%p",&a);
};
block();
結(jié)果是:
0x7fff53f42a2c
0x7fb601475220
- 如果想在block中修改外界變量的值放可,必須在外界變量前面加上__block谒臼,在內(nèi)部修改了變量的值會直接影響外部的值,但是內(nèi)部外部的變量依然不是同一個耀里,他們的內(nèi)存地址依然不同蜈缤。
__block int a = 5;
NSLog(@"%p",&a);
void (^block)() = ^{
a = 10;
NSLog(@"%p",&a);
};
block();
結(jié)果是:
0x7fff5100ea88
0x7fe931629858
那么,加上__block就可以的本質(zhì)原因就是傳值方式的原因冯挎。
- 把未加__ block修飾的那段代碼的ViewController.m的代碼編譯成C++代碼底哥,如圖:
畫框代碼即是核心部分,如下:
int a = 5;
void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
歷盡艱難險阻終于找到a
的影子房官,其中__ViewController__viewDidLoad_block_impl_0
是一個結(jié)構(gòu)體趾徽,在這里傳了三個參數(shù):(void *)__ViewController__viewDidLoad_block_func_0
、&__ViewController__viewDidLoad_block_desc_0_DATA
和a
翰守,不難看出孵奶,這里是直接將a作為參數(shù)傳遞,也就是值傳遞潦俺,既然是值傳遞拒课,修改里面的值對外部的a自然是無效的。
那么用__block修飾代碼的cpp文件事示,就如下圖:
畫框部分是核心代碼早像,如下:
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 5};
void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
僅僅加了一個修飾詞,代碼就有了不小的變動肖爵,這里的結(jié)構(gòu)體傳了四個參數(shù)卢鹦,a
在第三個位置,并且a
也有了修飾:(__Block_byref_a_0 *)&a
劝堪,不難看出冀自,這里的&標(biāo)志著這里是指針傳遞,既然是指針傳遞秒啦,修改里面的值肯定會影響到外部的那個變量熬粗。