------------Light.h------------
#import <Foundation/Foundation.h>
@interface Light : NSObject
@property (nonatomic, copy) NSString *color;
@property (nonatomic, copy) void (^block)(void);
@end
------------Light.m------------
#import "Light.h"
@implementation Light
@end
------------main.m------------
#import "Light.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
Light *loveLight = [Light alloc] init];
loveLight.color = @"green";
loveLight.block = ^{
NSLog(@"%@",loveLight.color);
};
loveLight.block();
}
}
複製代碼
咱們在上面的代碼中建立了一個Light(光)類,並聲明兩個屬性color(顏色)及block。而後咱們實例化一個對象loveLight並對其屬性賦值,實現並調用block,形成循環引用。 而後咱們經過clang代碼,瞭解這段代碼內部的部分實現:面試
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Light *loveLight;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Light *_loveLight, int flags=0) : loveLight(_loveLight) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
Light *loveLight = __cself->loveLight; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yf_3v_c713x5898zz9d49t0rd140000gn_T_main_81cd17_mi_1,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)loveLight, sel_registerName("color")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->loveLight, (void*)src->loveLight, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
...後省略
複製代碼
經過clang後的源碼咱們能夠知道:多線程
同時面試中面試官還可能會詢問你如何檢測到內存泄漏,咱們能夠經過Instruments中的Leaks進行檢測,也能夠選擇facebook發佈的FBRetainCycleDetector內存泄漏檢測工具。併發
首先UIView Animation使用時,不須要考慮循環引用的問題。函數
UIKit將動畫直接集成到UIView的類中,當內部的一些屬性發生改變時,UIView將爲這些改變提供動畫支持,並以類方法的形式提供接口。工具
而block形成循環引用的主要緣由是對象與block的相互持有,UIView Animation的block自己處於類方法中,在使用時並不屬於調用控制器。同時控制器也沒法強引用一個類,因此不會形成循環引用的問題。動畫
block屬性可使用strong屬性修飾符修飾,可是不推薦,會有內存泄漏的隱患。ui
首先,ARC中block用copy屬性修飾符修飾是MRC時代延續的產物,提醒開發者可能存在的內存問題。同時copy的確是能夠用strong來替代的。atom
咱們都知道block在OC中有三種類型:
- _NSConcreateGlobalBlock 全局的靜態block,不會訪問任何外部變量。 - _NSConcreateStackBlock 棧區的block,當函數返回時會被銷燬。 - _NSConcreateMallocBlock 堆區的block,當引用計數爲0時被銷燬。spa
block在MRC下能夠存在於全局區、棧區和堆區,而在ARC下,block會自動從棧區拷貝到堆區(除了裸寫block實現塊),因此只存在於全局區和堆區。 因此對於棧區block,MRC下處於棧區,想在做用域外調用就得copy到堆區;ARC則自動copy堆區。線程
那麼這個時候問題就來了,strong屬性修飾符並不能拷貝,就會有野指針錯區的可能,形成Crash。這種狀況不多見,可是不表明不可能發生,因此最好仍是使用copy屬性修飾符。
此處感謝@buaacyg的評論反饋,同時也對撰文的不嚴謹深表歉意。
針對Block屬性修飾符的問題在撰寫的時候的確沒有考慮周全,咱們將在如下予以更正和解答。 首先,在如下情形中block會自動從棧拷貝到堆:
那針對上述自動拷貝的狀況咱們作一個實驗:
ARC下strong修飾block,且不引用外部變量,block類型爲__NSGlobalBlock
ARC下strong修飾block,引入外部變量,block類型爲__NSMallocBlock因此由此就能夠理解爲ARC下strong修飾的block並無處於棧區的可能,也就不存在做用域結束棧區內容銷燬野指針的問題了。 可是爲了保證修飾符和block特性的一致性,使用copy修飾符仍然是最爲合適的。
賦值場景下使用__block,使用場景下不須要。 咱們來對比下賦值場景和使用場景
//賦值場景
NSMutableArray *__block array = nil;
void(^Block)(void) = ^{
array = [NSMutableArray array];
};
Block();
//使用場景
NSMultableArray *array = [NSMultableArray array];
void(^Block)() = ^{
[array addObject:@"1"];
};
Block();
複製代碼
dispatch_queue_t gQueue= dispatch_get_global_queue(0, 0);
NSLog(@"1");
dispatch_sync(gQueue, ^{
NSLog(@"2");
dispatch_sync(gQueue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
複製代碼
答案:12345
首先打印1,全局隊列本質是併發隊列也就是併發同步,同步任務不開起線程,在主線程執行打印2。 而後全局隊列執行同步任務,依舊不開啓線程,在主線程執行打印3。 同步任務完成,依舊存在於全局隊列同步執行,打印4. 同步任務完成,打印5。