iOS面試--Block
來自專欄 iOS面試
Block
block底層原理實現
首先來看四個函數:
void test1() { int a = 10; void (^block) () = ^{ NSLog(@"a is %d",a); } a = 20; block();//a = 10}void test2() { __block int a = 10; void (^block) () = ^{ NSLog(@"a is %d",a); } a = 20; block();// a = 20}void test3() { static int a = 10; void (^block) () = ^{ NSLog(@"a is %d",a); } a = 20; block();//a = 20}int a = 10;void test4() { void (^block) () = ^{ NSLog(@"a is %d",a); }; a = 20; block();//20}
? 造成這樣的原因是:傳值和傳址。為什麼說會有傳值和傳址,把.m編程成C++代碼得到.cpp文件,來看下編譯後的代碼:
struct __test1_block_impl_0 { struct __block_imp impl; struct __test1_block_desc_0 *Desc; int a; __test1_block_impl_0(void *fp, struct __test1_block_desc_0 *Desc, int _a, int flag=0): a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __test1_block_func_0(struct __test1_block_imp_0 *_cself) { int a = __cself->a; NSLog(a);//}void test1() { int a = 10; void (*block)() = (void (*)())&__test1_block_imp_0((void *))__test1_block_func_0, &___test1_block_desc_0_DATA,a); a = 20; ((void (*)(__block_impl *))((__block_impl *)block) -> FuncPtr) ((_block_imp1 *)block);}int main(int argc, const char* argv[]){ __autoreleasepool; test1(); return 0; } static struct IMAGE_INFO { unsigned version, unsigned flag; }_OBJC_IMAGE_INFO = { 0, 2 };
? 我們看到void test1()中,void(*block)()右面最後面,把a傳進入,也就是把10這個值傳進入了。
? 而且對void(block)簡化分析,void(*block)() = &_test1_blcok_impl_0();所以block就是指向結構體的指針? 10傳入block後,傳入代碼最上面創建的_test1_block_impl_0結構體中,a = 10;? 對void test1()中最下面的函數進行簡化分析,得到(block)-> FuncPtr)(block),我們再回到剛才test1_block_impl0這個結構體中,impl.FuncPtr = fp;而fp又是傳入結構體的第一個參數,而在void(*block)()中,傳入結構體的第一個參數為test1_block_func_0,也就是說(block)-> FuncPtr => _test1_block_func_0(block);
? 上一步相當於調用test1_block_func_0()這個函數,有這樣一段代碼:int a = cself->a;訪問這個block中a值,傳遞給a;所以10就是傳值!
我們再來看test2的反編譯後的代碼:
void test2(){ __attribute__((blocks__(byref))) __Block_byref_a_0 a = {(void*)0, (__Block_byref_a_0 *) &a, 0, sizeof(__Block_byref_a_0), 10}; void(*block)() = (void (*)()&__test2_block_impl_0((void*))__test2_block_func_0, &__test2_block_desc_0_DATA,(__Block_byref_a_0 *) &a, 5704999); (a.__forwarding->a) = 20; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr((_block_impl*)block);}int main(int argc, const char * argv[]){ test2(); return 0;}
? void(*block)()這個函數後面是&a,就是傳址。
Block的定義
? 無參數無返回
> void (^block);? 無參數有返回
> int (^block)();? 有參數有返回> int (^block)(int number);
Block的內存管理
? 無論當前環境是ARC或者MRC,只要block沒有訪問外部變數,block始終在全局區
? MRC情況下:> block如果訪問外部變數,block在棧里
> 不能對block使用retain,否則不能保存在堆里> 只有使用copy,才能放到堆里? ARC情況下:> block如果訪問外部變數,block在堆里> blcok可以使用copy和strong,並且block是一個對象
Block的循環引用
? 如果要在block中直接使用外部強指針會發生錯誤,使用以下代碼在block外部實現可以解決
__weak typeof(self) weakSelf = self;
? 但是如果在block內部使用延時操作時,如果還用weak引用,就會找不到該指針,需要在block內部將指針重新強引用一下:
__strong typeof(self) strongSelf = weakSelf;
描述一個retain cycle例子
? block中循環引用
@property (nonatomic, strong) HttpRequestHandler *handler;@property (nonatomic, strong) NSData *data;_handler = [HttpRequestHandler shareManager];[downloadData:^(id responseData) { self.data = responseData;}
self擁有_handler,_handler擁有block,block擁有self,因為block使用了外部變數,block會copy一份self;
解決辦法:
__weak typeof(self) weakSelf = self;[downloadData:^(id responseData) { weakSelf.data = responseData;}
通過block來傳值
? 在控制器間傳值可以使用代理或者block,使用block相對來說簡潔
ModalViewController * modalVC = [[ModalViewController alloc]init];modalVC.valueBlock = ^(NSString *str) { NSLog(@"str"); } [self presentViewController:modalVC];
? 在ModalViewController控制器的.h中聲明一個屬性
@property (nonatomic, strong) void(^valueBlock)(NSString *str);
? 在.m中實現
- (void)touchesBegan:(NSSet *)touch{ if (_vlaueBlock){ _valueBlock(@"124") }}
Block作為一個參數使用
? 聲明一個帶block的函數
- (void)calculator:(int(^)(int result))block;
? block作為返回值
使用block有什麼好處?使用NSTimer寫出一個使用block顯示秒錶的代碼
? block的好處,最直接的就是代碼緊湊,傳值,回調方便
推薦閱讀:
※面試時說課的技巧(轉)
※酒店HR不會告訴你的面試真相,殘酷卻很受用!
※如何在面試中回答「你最大的缺點是什麼」? |
※[面試]常規面試著裝的訣竅