標籤:

iOS面試--Block

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不會告訴你的面試真相,殘酷卻很受用!
如何在面試中回答「你最大的缺點是什麼」? |
[面試]常規面試著裝的訣竅

TAG:iOS | 面試 |