block定義
- 格式:
返回類型 (^block名字) (參數列表);
- 同C語言的定義函數指針一樣,C語言的函數指針定義格式:
返回類型 (*指針名字) (參數列表);
block賦值
名字 = ^{xxx};
- 如同普通的變量賦值一樣類型、分號都需要‘一一對應’,如同
int a = 5;
,^
標志右邊的代碼段是block類型。
block調用
block名字 (參數列表);
例(無參):
//定義
void (^valenti) ();
//賦值
valenti = ^{
NSLog(@"VaLenTi is MEEEEE!");
};
//調用
valenti();
例(有參):
//定義
void (^valenti) (NSString* name);
//賦值
valenti = ^(NSString* name){
NSLog(@"VaLenTi is %@!",name);
};
//調用
valenti(@"ME");
例(有參有返回值):
//定義
NSInteger (^sum)(NSInteger value1, NSInteger value2);
//賦值
sum = ^(NSInteger value1, NSInteger value2){
return value1 + value2;
};
//調用
NSInteger result = sum(1,1);
typedef與block
如若有這樣的需求:定義四個block實現兩個參數的加減乘除,他們的代碼是如下這樣的:
//加
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的名字和操作不同以外,其余的結構都是相同的,那么,相同的部分即可用typedef起別名的形式“抽取”出來。格式如下:
typedef 返回值類型 (^block名字) (參數列表)
- 同C語言的函數指針別名一樣,名稱即代表別名。
上述代碼可改為:
//1.在類擴展處定義別名
typedef NSInteger (^calculate) (NSInteger value1,NSInteger value2);
//2.在實現中定義對應功能的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內部外部存在同名變量的情況下,block訪問的變量是內部變量-“就近原則”。
默認情況下,不可以在block內部修改外部的變量,1中block內部是不可以對a進行賦值的,因為block中的a和外部的a本質上并不是同一個a,block訪問的外部變量會將外部的變量拷貝一份到堆內存中,驗證:
int a = 5;
NSLog(@"%p",&a);
void (^block)()= ^{
NSLog(@"%p",&a);
};
block();
結果是:
0x7fff53f42a2c
0x7fb601475220
- 如果想在block中修改外界變量的值,必須在外界變量前面加上__block,在內部修改了變量的值會直接影響外部的值,但是內部外部的變量依然不是同一個,他們的內存地址依然不同。
__block int a = 5;
NSLog(@"%p",&a);
void (^block)() = ^{
a = 10;
NSLog(@"%p",&a);
};
block();
結果是:
0x7fff5100ea88
0x7fe931629858
那么,加上__block就可以的本質原因就是傳值方式的原因。
- 把未加__ 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
是一個結構體,在這里傳了三個參數:(void *)__ViewController__viewDidLoad_block_func_0
、&__ViewController__viewDidLoad_block_desc_0_DATA
和a
,不難看出,這里是直接將a作為參數傳遞,也就是值傳遞,既然是值傳遞,修改里面的值對外部的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);
僅僅加了一個修飾詞,代碼就有了不小的變動,這里的結構體傳了四個參數,a
在第三個位置,并且a
也有了修飾:(__Block_byref_a_0 *)&a
,不難看出,這里的&標志著這里是指針傳遞,既然是指針傳遞,修改里面的值肯定會影響到外部的那個變量。