*注:解釋內容主要參考《Objective-C 高級編程》
1.介紹:
Blocks是C語言的擴充功能:帶有自動變量(局部變量)的匿名函數。
Blocks的語法:^ 返回值類型 參數列表 表達式
例如:
^int (int c){return c+1;}其中,返回值類型 是可以省略的,會按return類型返回,如果不需要參數,那么參數列表 也可以省略,如:^{PRintf("hello");}2.Block類型變量
int (^blk)(int) = ^(int c){return c+1;}; int x = blk(5); NSLog(@"x = %d",x);Block值^(int c){return c+1;} 被賦值給了變量blk中,blk即為Block變量。利用typedef定義Block別名:
typedef int (^blk_1)(int); blk_1 blkk = ^(int x){return x+3;}; NSLog(@"x = %d",blkk(3));意指用blk_1來代替int ^(int);這種塊,之后調用賦值什么的會更舒服。3.截獲自動變量值
int x = 1; void (^blk)(void) = ^{ NSLog(@"zhi = %d",x); }; x = 6; blk();以上代碼輸出結果 zhi = 1;可見在Block截獲的是自動變量x的值,之后改變x,并不會對塊的執行結果有影響。并且我們如果想在塊中改變x的值是無法通過編譯的。如果我們想在塊中改變x的值,需要用__block說明符4.__block說明符
__block int x = 1; void (^blk)(void) = ^{ x = 6; }; blk(); NSLog(@"x = %d",x);輸出結果為 x = 6
用__block修飾x后,便可以在塊中改變截取的自動變量x,那么:__block int x = 1; void (^blk)(void) = ^{ NSLog(@"x = %d",x); }; x = 7; blk();輸出結果為 x = 7這里思考,如果我們不用__block,截取OC對象,會怎么樣:
NSString *age = @"26"; NSMutableDictionary *dic =[[NSMutableDictionary alloc]init]; [dic setObject:age forKey:@"age"]; void (^blk)(void) = ^{ NSString *a = [dic objectForKey:@"age"]; NSLog(@"age = %@",a); }; NSString *age2 = [dic objectForKey:@"age"]; age2 = @"18"; [dic removeObjectForKey:@"age"]; [dic setObject:age2 forKey:@"age"]; blk();輸出結果為 age = 18
這里我們初始化了一個dic,里面有一個key為@"age"的NSString,初始值為26,在塊中輸出字典中字符串的值,然后在塊執行前,改變dic中字符串。由此我們聯想:
NSString *age = @"26"; NSMutableDictionary *dic =[[NSMutableDictionary alloc]init]; [dic setObject:age forKey:@"age"]; void (^blk)(void) = ^{ NSString *age2 = [dic objectForKey:@"age"]; age2 = @"18"; [dic removeObjectForKey:@"age"]; [dic setObject:age2 forKey:@"age"]; }; blk(); NSString *a = [dic objectForKey:@"age"]; NSLog(@"age = %@",a);輸出 age = 18
所以在塊中也能改變dic的值。這里我們分析,我們用塊截取的是NSMutableDIcitionary的實例指針,不能對其賦值,但是可以使用它,即可以向其中添加改變對象。
*這里我們要注意:
const char text[] = "HelloWorld"; const char *text1 = "HelloWorld"; void (^blk)(void) = ^{ printf("%c",text1[1]); }; blk();我們可以使用截取*text1,但是text[]不可以,因為截取自動變量的方法并沒有實現對C語言數組的接獲,應該用指針方法解決。4.Blocks的實現那么這種截取自動變量以及__block修飾變量,背后是怎樣實現的呢,本文簡單介紹一下有關截取變量這里,至于Blocks實現的詳細解析放在后續文章中。
我們利用clang的指令轉變一下我們的文件,打開終端,利用 clang -rewrite-objc ‘文件名’ 對我們的文件進行操作,我們的文件內容如下:
int main(){ int i = 10; int (^blk)(void) = ^{ int x = i+1; return x; }; int jieguo = blk(); return 0;}聲明一個簡單的塊,截獲了自動變量 int i,我們用clang指令操作它得到.cpp文件:struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int i; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};//塊中函數static int __main_block_func_0(struct __main_block_impl_0 *__cself) { int i = __cself->i; // bound by copy int x = i+1; return x;}由于轉化后代碼太多,就只截取了一部分??梢钥吹浇Y構體構造函數初始化 i(_i) , 讓i = _i,這是一個copy的過程,所以外部改變自動變量的值,不會影響到塊中的值。如果使用__block修飾的話:
struct __Block_byref_i_0 { void *__isa;__Block_byref_i_0 *__forwarding; int __flags; int __size; int i;};struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_i_0 *i; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static int __main_block_func_0(struct __main_block_impl_0 *__cself, int x) { __Block_byref_i_0 *i = __cself->i; // bound by ref int y = x + (i->__forwarding->i); return y;}可以看到,在構造函數中,i(_i->__forwarding) , 這里用一個結構體__Block_byref_i_0,以指針的方式去保存自動變量,保存了變量的地址,那么后續改變自動變量,塊中的值也會相應改變。有關塊循環引用的問題,及各種使用會在后面文章中介紹。
新聞熱點
疑難解答