面試驅(qū)動技術(shù)合集(初中級iOS開發(fā)),關(guān)注倉庫,及時獲取更新 Interview-series
Block 在 iOS 算比較常見常用且??嫉牧?,現(xiàn)在面試中,要么沒面試題,有面試題的,基本都會考到 block 的點。本文特別干!(但是初中級iOSer應該能有所收獲~)
先來個面試題熱熱身,題目: 手撕代碼 - 用Block實現(xiàn)兩個數(shù)的求和
(這題如果會的,block基礎(chǔ)知識可以跳過了,直接到 Block原理探究)
簡單介紹block入門級用法
Block結(jié)構(gòu)比較復雜,一般用 typedef 定義,直接調(diào)用的感覺比較簡單、清晰易懂
//typedef block的時候有提示
typedef void(^MNBlock)(int);
@interface ViewController ()
@property (nonatomic, copy) MNBlock block;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//直接用self.block調(diào)用
self.block = ^(int a) {
//dosomething...
};
}
- 參數(shù)解釋:
typedef <#returnType#>(^<#name#>)(<#arguments#>);
題目: 手撕代碼 - 用Block實現(xiàn)兩個數(shù)的求和
日常開發(fā)中,block聲明一般寫的比較多,實現(xiàn)一般是靠Xcode自動補全提示出現(xiàn)的,手撕代碼的情況下,等號右側(cè)的block實現(xiàn)要怎么寫?
聲明:
typedef int(^MNBlock)(int a, int b);
@interface ViewController ()
@property (nonatomic, copy) MNBlock sum;
Vip補全功能:
紙上按Enter沒用啊兄弟!看來還是需要了解一下Block右邊的東西~
先在 Xcode上按下 Enter,了解下再撕
^int(int a, int b) {
//Control reaches end of non-void block
因為返回值是int類型,所以這里需要返回
}
int(^Sum)(int, int) = ^(int a, int b){
return a + b;
};
int result = Sum(5, 10);
Block的坑出現(xiàn)!新手可能會寫錯的地方
1.聲明出錯 - void ^(testBlock)
修正版:
void (^testBlock)() = ^{
};
block的聲明,^ 和 blockName 都是在小括號里面??!
2.block各種實現(xiàn)的參數(shù)問題
聲明typedef int(^MNBlock)(int, int);
self.sum = ^int(int a, int b) {
return a + b;
};
這里要注意,block聲明里面只有參數(shù)類型,沒有實際參數(shù)的話,Xcode提示也只有參數(shù),這里涉及到形參和實參的問題
聲明是形參,可以不寫參數(shù),但是使用的時候,必須有實際參數(shù),才可以進行使用,所以這里需要實參,可以在 ^int(int , int)
中手動添加實參^int(int a, int b)
,就可以讓a 和 b 參與運算
小tips:實際開發(fā)中,建議聲明的時候,如果需要帶參數(shù),最好形參也聲明下,這樣使用Xcode提示的時候,會把參數(shù)帶進去,方便得多~(踩過坑的自然懂!)
- 省略void導致看不懂block結(jié)構(gòu)的 (正常是兩個void導致局面混亂)
//聲明
typedef void(^MNBlock)(void);
//實現(xiàn)
self.sum = ^{
//dosomething...
};
這種情況下,能知道怎么省略的,聲明里兩個void,能知道怎么對應的嗎?
這個其實比較簡單,block不管聲明 or 實現(xiàn),最后一個小括號,里面都是參數(shù),而參數(shù)是可以省略的!
而為了把聲明的兩個void區(qū)分開,返回值 or 參數(shù)區(qū)分開,其實就ok了
參數(shù)非void的例子
//聲明非void的參數(shù)
typedef void(^MNBlock)(int a);
//實現(xiàn)就必須帶參數(shù),不可省略!
self.sum = ^(int a) {
}
參數(shù)void的例子 ==> 參數(shù)可以省略
typedef int(^MNBlock)(void);
self.sum = ^{
//聲明的返回值類型是int,所以一定要return;
return 5;
};
其實-返回值是void的,也可以不省略
typedef void(^MNBlock)(void);
//實現(xiàn)的返回值不省略
self.sum = ^void () {
};
參數(shù)是void的省略:
typedef int(^MNBlock)();
//實現(xiàn)里面,沒有參數(shù),就可以不寫()
self.sum = ^int{
return 5;
}
注意??! 聲明里面的返回值void是不可以省略的!!
- 小箭頭^混亂的問題,到底放小括號內(nèi)還是小括號外
聲明是 int(^MNBlock)(int a , int b)
實現(xiàn)是 ^int(int a, int b)
注意,這里箭頭之后的,不管是多寫() 還是少寫,都會出錯
所以這里還不能死記,比如不管聲明還是實現(xiàn),死記 (^ xxx) 是沒問題的 or 死記 ^…… xxx 不加括號是沒問題的,在這里都行不通,只能靠腦記了
這時候,就需要用到巧記了!
^ 和小括號組合的,一共有三種情況
- 一種是聲明的,
void(^MNBlock)
- 一種是實現(xiàn)的,
^int(int a,)
- 還一種
^(int a)
兄弟,看到這你還不亂嗎??!
怎么記看這里,
- 手寫分為兩個部分,block等號左邊 or 等號右邊的,左邊為聲明,右邊為實現(xiàn)區(qū)分開
- 聲明記住:^后面跟blockName,他們需要包起來! (blockName),只有聲明會用到blockName,先記住一點,如果有blockName,要和一起,用小括號包起來
- 實現(xiàn)又分為兩種:
-
^int
:^后面跟的是返回值類型- ^ 直接跟類型,不用加"( )" ==>
^int
- ^ 直接跟類型,不用加"( )" ==>
-
^(int a)
:^后面直接跟參數(shù) (返回值是void)。- 參數(shù)都是要用"( )"包起來的,如果^后面跟參數(shù),就得用"( )" ==>
^(int a)
, - 實現(xiàn)里,肯定有實際參數(shù),這時候,參數(shù)類型和實參,就得用( )包起來
- 參數(shù)都是要用"( )"包起來的,如果^后面跟參數(shù),就得用"( )" ==>
-
^與小括號糾纏的總結(jié)
- ^ 后面僅跟類型,不需要小括號,==>
^int
- ^ 后面跟參數(shù),參數(shù)需要小括號 ==>
^(int a)
- ^ 后面跟block名稱,^和blockName需要小括號 ==>
void (^MNBlock)
Block原理探究
void (^MNBlock)(void) = ^(void){
NSLog(@"this is a Block~ rua~");
};
MNBlock();
使用 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
轉(zhuǎn)成 C++ 代碼, 查看底層結(jié)構(gòu)
//對應上面的 MNBlock聲明
void (*MNBlock)(void) = (&__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA));
//對應上面的 MNblock() 調(diào)用
MNBlock->FuncPtr(MNBlock);
//block聲明調(diào)用的 - __main_block_impl_0
struct __main_block_impl_0 {
//結(jié)構(gòu)體內(nèi)的參數(shù)
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//c++中的構(gòu)造函數(shù),類似于 OC 的 init 方法,返回一個結(jié)構(gòu)體對象
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
}
這里的block封裝的函數(shù)調(diào)用解釋MNBlock->FuncPtr(MNBlock);
MNBlock 其實內(nèi)部結(jié)構(gòu)是 __main_block_impl_0
,
struct __main_block_impl_0 {
//函數(shù)調(diào)用地址在這個結(jié)構(gòu)體內(nèi)
struct __block_impl impl;
struct __main_block_desc_0* Desc;
}
struct __block_impl {
void *isa;
int Flags;
int Reserved;
//函數(shù)調(diào)用地址在這里
void *FuncPtr;
};
內(nèi)部只有兩個參數(shù),一個impl
,一個Desc
,而函數(shù)的調(diào)用地址 - FuncPtr
是再impl
中的,為什么這里能直接這樣寫呢?
因為,__main_block_impl_0 結(jié)構(gòu)的地址和他的第一個成員一樣,第一個成員的地址是__block_impl,所以__main_block_impl_0 和 __block_impl 的地址其實是同一個,通過格式強制轉(zhuǎn)換,將 main_block_impl_0 轉(zhuǎn)成 block_impl 就可以直接拿到他內(nèi)部的 FuncPtr 函數(shù)地址,然后進行調(diào)用!
可見- block本質(zhì)上是OC對象,內(nèi)部有一個isa指針
block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用的oc對象
Block面試題拋磚引玉~
開胃菜先來一下,以下結(jié)果輸出什么
int a = 10;
void (^MNBlock)(void) = ^{
NSLog(@"a = %d",a);
};
a += 20;
MNBlock();
調(diào)用 MNBlock();
之前,a 已經(jīng) + 20了,輸出30? 太天真了兄弟,這里涉及到capture的概念,即變量捕獲
Block捕獲變量(capture)
捕獲:Block內(nèi)部會新增一個成員,來存儲傳進來的變量
block 內(nèi)部直接捕獲了傳進去的這個變量a(10)
創(chuàng)建block的時候,已經(jīng)將變量a=10 捕獲到 block內(nèi)部,之后再怎么修改,不會影響block 內(nèi)部的 a
auto 和 static的區(qū)別:以下會輸出什么~
static int b = 10;
void (^MNBlock)(void) = ^{
NSLog(@"a = %d, b = %d",a,b);
};
a = 20;
b = 20;
MNBlock();
輸出
2019-03-07 21:49:49 Block-Demo a = 10, b = 20
why?
查看原因:
auto int a = 10;
static int b = 10;
void (*MNBlock)(void) = (&__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA,
a,
&b));
發(fā)現(xiàn):兩種變量,都有捕獲到block內(nèi)部。
a 是auto變量,走的是值傳遞,
b 是 static 變量,走的是地址傳遞,所以會影響(指針指向同一塊內(nèi)存,修改的等于是同個對象)
總結(jié)
- 只有局部變量才需要捕獲,
- 全局變量不需要捕獲,因為在哪都可以訪問
- 需不需要捕獲,其實主要是看作用域問題
- auto局部變量 ==>值傳遞->因為會銷毀
- static局部變量==>不會銷毀==>所以地址傳遞
看圖就行~
進階考題 - self 會被捕獲到 block 內(nèi)部嗎
void (^MNBlock)(void) = ^{
NSLog(@"p = %p",self);
};
模擬看官作答:不會,因為整個類里,都能調(diào)用self,應該是全局的,全局變量不會捕獲到block中
哈哈哈哈!中計了!其實 self 是參數(shù)(局部變量)
struct __MNDemo__test_block_impl_0 {
struct __block_impl impl;
struct __MNDemo__test_block_desc_0* Desc;
MNDemo *self; ==> 捕捉到了兄弟
}
解釋原因:
- 每個OC函數(shù),其實默認有兩個參數(shù),一個self,一個_cmd,只是他們倆兄弟默認是隱藏的
- 而由于他們是參數(shù),所以是局部變量,局部變量就要被 block 捕獲
- (void)test(self, SEL _cmd){XXX}
默認的OC方法里面其實有這兩個隱藏的參數(shù)!所以上題的答案,self是會被block捕獲的!(能聽懂掌聲!)
進進階考題 - 成員變量_name 會被捕獲到 block 內(nèi)部嗎
void (^MNBlock)(void) = ^{
NSLog(@"==%@",_name);
};
模擬看官作答:呵呵,老子都中了這么多次技了,這題學會了??! 因為_name是成員變量,全局的,也沒有self,所以不需要捕獲整個類就都可以隨便訪問它!
哎,兄弟,還是太年輕了!!
void (^MNBlock)(void) = ^{
NSLog(@"==%@",self->_name);
};
看圖說話,不多bb, (能聽懂掌聲?。?/em>
Block的類型
__NSGlobalBlock__
__NSStackBlock__
__NSMallocBlock__
MRC環(huán)境下
void (^global)() = ^{
NSLog(@"globalValue = %d",globalValue);
};
void (^autoBlock)() = ^{
NSLog(@"this is a Block~ rua~ = %d",a);
};
void (^copyAuto)() = [autoBlock copy];
--------------------------------------------
print class
2019-03-08 17:40:43 Block-Demo
global class = __NSGlobalBlock__
autoBlock class = __NSStackBlock__
copyAuto = __NSMallocBlock__
總結(jié):
內(nèi)存分配示意圖:
棧上的內(nèi)存系統(tǒng)會自動回收
- ??臻g的block 不會對 對象進行強引用
- 堆空間的block 可能會對對象產(chǎn)生強引用:
- 如果是weak指針,不會強引用
- 如果是strong指針,會強引用
堆上的內(nèi)存是由程序員控制,所以一般將block 拷貝到堆上,讓程序員控制他與內(nèi)部變量的生命周期
題目:以下輸出的順序是什么(ARC環(huán)境下)
@implementation MNPerson
- (void)dealloc{
NSLog(@"MNPerson - dealloc");
}
@end
--------------------------------------
MNPerson *person = [[MNPerson alloc]init];
__weak MNPerson *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1-----%@",person);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2------%@",weakPerson);
});
});
NSLog(@"touchesBegan");
輸出結(jié)果
2019-03-08 22:38:59.038452+0800 touchesBegan
2019-03-08 22:39:00.056746+0800 1-----<MNPerson: 0x604000207840>
2019-03-08 22:39:00.057891+0800 MNPerson - dealloc
2019-03-08 22:39:02.058011+0800 2-----(null)
解釋:
gcd的block會自動對auto變量進行copy操作
block內(nèi)部對 auto 變量的強弱引用,取決于指針類型
1 中的auto變量是 person,沒聲明默認對象是 strong 類型,所以 gcd1 會對 person進行 1s的強引用
gcd2 中的變量是 weakPerson,看到是__wesk指針,所以block內(nèi)部不會對其產(chǎn)生強引用
隨后,gcd1 對 person進行1s的強引用之后,gcd1 的block銷毀,person對象銷毀,打印MNPerson dealloc
最終,2s過后打印 2——weakPerson,因為person對象在gcd1 block結(jié)束之后,釋放掉了,所以此時person是空,因為是weak指針,對象是null不會crash,最終打印null
對象類型的auto變量
- 當 block 內(nèi)部訪問了對象類型的auto變量時
- 如果block在展示,不會對 auto 變量產(chǎn)生強引用
- 如果 block 被 拷貝到堆上
- 會調(diào)用 block 內(nèi)部的 copy 函數(shù)
- copy 函數(shù)內(nèi)部會調(diào)用 _Block_object_assign 函數(shù)
- _Block_object_assign 函數(shù)會根據(jù)auto變量的修飾符 ( strong、 weak、unsafe_unretained ) 做出對應的操作,看對內(nèi)部auto變量進行強引用還是弱引用(類似于 retain)
- 如果 block 從 堆上移除
- 會調(diào)用 block 內(nèi)部的 dispose 函數(shù)
- dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose 函數(shù)
- _Block_object_dispose 類似于 release,會對auto變量進行自動釋放(當引用計數(shù)器=0的時候 )
block中的copy
- 在ARC環(huán)境下,編譯器會根據(jù)情況,自動將棧上的block拷貝到堆上,比如以下幾種情況
- block 作為函數(shù)返回值的時候
- 將block復制給__strong指針的時候
- block作為Cocoa API中方法名含有usingBlock的方法參數(shù)事
- 比如:
[array enumerateObjectsUsingBlock:XXX]
- 比如:
__block 修飾符的使用
題目:以下代碼的是否編譯通過,可以的話輸出結(jié)果是什么
int a = 10;
void (^block)() = ^{
a = 20;
NSLog(@"a = %d",a);
};
結(jié)果如下:
思考:無法編譯,為啥呢?編譯的時候,block應該是會把auto變量捕獲進去的,那block結(jié)構(gòu)中應該有a才對啊
//main函數(shù)
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
}
return 0;
}
//block執(zhí)行地址
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kh_0rp73c0s2mvfp5gjf25j5y6h0000gn_T_main_1a12fa_mi_0,a);}
block執(zhí)行的時候,內(nèi)部是 __main_block_func_0
函數(shù),而a的聲明,是在main
函數(shù),兩個函數(shù)相互獨立,對于他們來說,a都是一個局部變量,而且兩個函數(shù)中都對a初始化,兩個函數(shù)的中a不是同一個,那怎么可以在 執(zhí)行函數(shù)中,修改main函數(shù)中的局部變量呢,所以編譯報錯!
如何改?
- 方案一:使用static
static int a = 10;
void (^block)() = ^{
a = 20;
NSLog(@"a = %d",a);
};
因為static修飾的auto變量,最終在block中進行的不是值傳遞,而是地址傳遞,措意執(zhí)行函數(shù)中的a 和 main 函數(shù)中的a,是同一個地址 ==> 等于同一個a,所以可以修改,輸出20
但是使用static,就會變成靜態(tài)變量,永遠在內(nèi)存中
- 方案二: 使用__blcok
__block auto int a = 10;
void (^block)() = ^{
a = 20;
NSLog(@"a = %d",a);
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref ==> auto的話,是int a,__block,變成對象了
}
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;==> 指向自己的結(jié)構(gòu)體
int __flags;
int __size;
int a; ==> 10在這里
};
a = 20;最終轉(zhuǎn)成 (a->__forwarding->a) = 20;
解釋下:__forwarding 是指向結(jié)構(gòu)體本身的指針,等價于a本身,其實就是通過a的結(jié)構(gòu)體指針,拿到里面的成員a,再對他賦值
指針傳遞,所以可以修改 auto 變量,通過block,間接引用 auto 變量
__block的內(nèi)存管理
- 當 block 在棧上的時候,不會對內(nèi)部的__block 變量產(chǎn)生強引用
- 當 block 從棧上被 copy 到堆上的時候
- 會調(diào)用block內(nèi)部的copy函數(shù)
- copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign 函數(shù)
- _Block_object_assign 函數(shù)會對 __block 變量進行一次 retain操作,產(chǎn)生強引用
抄圖分析 :
- 當block從堆中移除時
- 會調(diào)用 block 內(nèi)部的 dispose 函數(shù)
- dispose內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
- _Block_object_dispose函數(shù)會對
__block
變量進行一次release操作,如果retainCount為0,自動釋放該__block變量
總結(jié):
- block在棧上的時候,不會對內(nèi)部的變量產(chǎn)生強引用
- 當block從棧上 copy 到堆上的時候,內(nèi)部都會調(diào)用 __Block_object_assign
- 如果是
__block
修飾的變量,會__block修飾的對象產(chǎn)生強引用 - 如果是普通auto變量,看修飾的指針類型是strong 還是 weak(unsafe_unretained)
- strong修飾的,block就會對內(nèi)部的auto變量產(chǎn)生強引用
- weak修飾的,block就不會對內(nèi)部的auto變量產(chǎn)生強引用
- 特別注意!上述條件僅在ARC環(huán)境下生效,如果是MRC環(huán)境下,block不會對內(nèi)部auto變量產(chǎn)生強引用!(MRC下不會進行retain操作)
- 如果是
- 當block從堆上移除的時候,內(nèi)部會調(diào)用
__Block_object_dispose
函數(shù),相當于對block
內(nèi)部所持有的對象進行移除release操作,如果retainCount為0,自動釋放該__block變量
__block中的 _ forwarding 指針
內(nèi)存拷貝的時候,如果block從棧被copy到堆上,肯定也希望內(nèi)部的變量一起存儲到堆上(讓變量的生命周期可控,才不會被回收)
加入變量a在棧上,在棧上的指針,指向堆上的 block,堆上的block的 forwarding指向他自己,就可以保證,修改&獲取的變量,都是堆上的變量
最終,__block指向的變量,是指向堆上的
__block 修飾的類型
@implementation MNObject
- (void)dealloc{
NSLog(@"MNObject - dealloc");
}
@end
--------------------------------------------
typedef void (^MNBlock)();
MNBlock block;
{
MNObject *obj = [[MNObject alloc]init];
__block __weak MNObject *weakObj = obj;
block = ^{
NSLog(@"----------%p",weakObj);
};
}
block();
問,上述代碼的輸出順序是?
2019-03-09 21:57:56.673296+0800 Block-Demo[72692:8183596] MNObject - dealloc
2019-03-09 21:57:56.673520+0800 Block-Demo[72692:8183596] ----------0x0
解釋:ARC下
上述代碼,block 持有的是 weakObj,weak指針,所以block內(nèi)部的__block結(jié)構(gòu)體,對他內(nèi)部持有的person不強引用!所以出了 小括號后,person沒有被強引用,生命gg,先dealloc,輸出dealloc
,之后進行block調(diào)用,打印 ---------
特別注意,上述邏輯進在ARC下,如果在MRC下,中間結(jié)構(gòu)體對象,不會對person 進行retain操作! 即便 person 是強指針修飾,也不會對內(nèi)部的person對象進行強引用!
MRC環(huán)境下
MNBlock block;
{
MNObject *obj = [[MNObject alloc]init];
block = [^{
NSLog(@"----------%p",obj);
}copy];
[obj release];
}
block();
[block release];
--------------------
輸出:
2019-03-09 21:59:56.673296+0800 Block-Demo[72692:8183596] MNObject - dealloc
2019-03-09 21:59:56.673520+0800 Block-Demo[72692:8183596] ----------0x0
上述代碼,obj 是 strong 修飾,但是并沒有被 block 強引用!可見MRC環(huán)境下,修飾的對象,生成的中間block對象不會對 auto變量產(chǎn)生強引用。
Block的循環(huán)應用問題
傳送門: 實際開發(fā)中-Block導致循環(huán)引用的問題(ARC環(huán)境下)
考題:MRC 下,block的循環(huán)引用如何解決呢?
- 方案1:unsafe_unretained
MRC下,沒有__weak,所以只能用_unsafe_unretained指針,原理和 weak 一樣(ARC環(huán)境下不推薦使用,可能導致野指針,推薦使用weak)
__unsafe_unretained MNObject *weakSelf = self;
self.block = [^{
NSLog(@"----------%p",weakSelf);
}copy];
- 方案2: __block
__block self;
self.block = [^{
NSLog(@"----------%p",self);
}copy];
why? 上面關(guān)于 __block的總結(jié)
特別注意!上述條件僅在ARC環(huán)境下生效,如果是MRC環(huán)境下,block不會對內(nèi)部auto變量產(chǎn)生強引用!(MRC下不會進行retain操作)
- 方案3: 手動在block函數(shù)內(nèi)將對象制空,并且必須手動保證block調(diào)用
MNObject *obj = [[MNObject alloc]init];
__unsafe_unretained MNObject *weakObj = obj;
obj.block = [^{
NSLog(@"----------%p",obj);
obj = nil;
}copy];
obj.block();
但是這個一定要注意,block必須調(diào)用,因為對象指針的清空操作,是寫在block函數(shù)中的,如果沒調(diào)用block,循環(huán)引用問題還是會存在,所以不推薦使用。
實際開發(fā)中,循環(huán)引用的檢測工具推薦,facebook開源的 FBRetainCycleDetector,用過的都說好~
話外篇補充 - Block 和 delegate使用場景
個人愚見
直接異步返回的,可以用block,比如網(wǎng)絡(luò)請求,無需其他人工動作觸發(fā)的
如果是需要類似點擊才能觸發(fā)的,比如 Button的點擊事件,可以用 delegate
老實說,block其實非常難,能考得特別深,本文也只是簡單探究&總結(jié)下中級iOS常見的block考題,以及對Block底層的初步探究,如果是像我所在的三線城市,去面試那種非一線公司的話,如果能掌握本文,可能block相關(guān)的題目能答個八九不離十吧!(可能題目會變換組合,但是萬變不離其宗)
block的文章其實很多,但是如果要真的深入理解,還是得動手,這里推薦初中級iOSer可以跟著本文的思路,一步一步跟著探究試試,本文只是起個拋磚引玉的作用
友情演出:小馬哥MJ
參考資料
Objective-C 高級編程 iOS與OS X多線程和內(nèi)存管理