OC--Block總結

參考

Block編譯代碼解讀:
block沒那么難(一、二、三)
iOS進階——iOS(Objective-C) 內存管理&Block

Block源碼解讀:
漫談Block
蘋果爹爹Block實現源碼

支持weak的clang命令

clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations test.m

一、Block代碼

(1) 沒有返回值、沒有參數的block

     void (^voidBlock)() = ^{};// 定義
     voidBlock();// 使用

(2) 沒有返回值、有一個參數的block

    void (^parameterBlock)(NSInteger parameter) = ^(NSInteger parameter){
        NSLog(@"=====parameterBlock");
    };
    parameterBlock(1);

(3)沒有返回值、有兩個參數的block

    void (^parametersBlock)(int par1, int par2) = ^(int par1, int par2){
        NSLog(@"=====parameterBlock");
    };
    parametersBlock(2,3);

(4) 有返回值、有參數的block

    NSString *(^returnBlock)(NSInteger a) = ^(NSInteger a){
        return @"returnBlock";
    };
    NSLog(@"=====%@",returnBlock);

(5) block的typedef定義、使用

    typedef void(^TypedefBlock)(NSInteger type);
    TypedefBlock typedefBlock = ^(NSInteger type){
        NSLog(@"TypedefBlock");
    };
    typedefBlock(1);

二、Block的類型,什么是NSGlobalBlock、NSStackBlock、NSMallocBlock?

1、NSGlobalBlock

(1)沒有捕獲外部變量

    void(^blockA)(void) = ^() {
        NSLog(@"=====%@",@"asdasd");
    };
    NSLog(@"=====%@",blockA); // <__NSGlobalBlock__: 0x10debd190>

(2)只捕獲全局變量,全局靜態變量,局部靜態變量。

int b = 20; // 全局變量
static int c = 30;// 全局靜態變量
- (void)viewDidLoad {
    [super viewDidLoad];

    static int a = 10; // 局部靜態變量
    void(^blockA)(void) = ^() {
        a++;
        b++;
        c++;
    };
    NSLog(@"=====%@",blockA); // <__NSGlobalBlock__: 0x10af19190>
}

另外:GlobalBlock的copy與retain還是GlobalBlock(后面有代碼解析)

2、NSStackBlock:使用了外部變量的block,將 Block 賦值給附有 __weak 修飾符變量。棧區
    int a = 10;
    typedef void (^BBBlock)();
    __weak BBBlock aBlock = ^(){
         NSLog(@"a = %i",a);
    };
    NSLog(@"=====%@",^{NSLog(@"a = %i",a);}); // ====<__NSStackBlock__: 0x7fff50af3ce0>
    NSLog(@"=====%@",aBlock); // =====<__NSStackBlock__: 0x7fff50af3d08>
3、NSMallocBlock:有以下幾種情況

(1)[stackBlock copy];stackBlock的copy方法;
(2)將stackBlock賦值給附有 __strong 修飾符的成員變量,(Block的retain其實就是copy,這就是為什么ARC的block屬性使用copy、strong修飾都一樣的原因)
(3)return stackBlock;stackBlock作為返回值;
(4)在方法名中含有 usingBlock 的 Cocoa 框架方法或 GCD 的 API 中傳遞stackBlock,方法里面先Block block = [stackBlock copy],然后使用block,(其實就是copy)

代碼例子
(1)[stackBlock copy]
    int a = 10;
    typedef void (^BBBlock)();
    __weak BBBlock stackBlock = ^(){
        NSLog(@"a = %i",a);
    };
    
    NSLog(@"=====%@",[stackBlock copy]);// <__NSMallocBlock__: 0x60000045d8b0>
(2)stackBlock做返回值
//返回值是stackBlock的,變成NSMallocBlock
- (void(^)(void))getBlock {
    int a = 0;
    return ^{NSLog(@"=====%d",a);};
}
NSLog(@"%@",[self getBlock]);// <__NSMallocBlock__: 0x60000045d8b0>

其實也不能簡單說做返回值,就是stackBlock,要看使用方法的環境。
嚴格的原理是:
(1)當block做為方法的返回值,方法執行時候,觸發了autorelease的優化操作(objc_retainAutoreleasedReturnValue),導致是block被retain(copy)
(2)如果定義一個 __weak BBBlock aBlock = [self getBlock];這個aBlock是nil(因為觸發autorelease的優化知道這個aBlock定義了__weak,被定義無意義的操作)

(3)將stackBlock賦值給附有 __strong 修飾符的成員變量時,(變量默認修飾符是__strong)
   int a = 10;
    typedef void (^BBBlock)();
    __weak BBBlock stackBlock = ^(){
        NSLog(@"a = %i",a);
    };
    
    BBBlock mBlock = stackBlock;
    NSLog(@"=====%@", mBlock);// <__NSMallocBlock__: 0x60000045d8b0>

三、block捕捉變量,有__block修飾、無__block修飾到底發生了什么?

無__block 修飾:

block捕獲變量,相當于在block結構體中開一個同名的變量(如變量是對象,則也同strong、weak修飾)

有__block 修飾:

__block局部變量,是包裝成一個__Block_byref_XXX_n結構體的對象,當這個__Block_byref對象被block使用且block_copy上堆時候,__Block_byref對象也會被復制到堆上(block源碼:_Block_object_assign中執行_Block_byref_copy如下)。

block源碼流程(libclosure-73)

block->flags 這個值主要用來告知系統Block在copy時應該執行什么操作,意思如下:

BLOCK_REFCOUNT_MASK = (0xfffe), // 一般參與判斷引用計數,是一個可選用參數
BLOCK_NEEDS_FREE = (1 << 24), // NSConcreteMallocBlock
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // NSConcreteStackBlock
BLOCK_IS_GLOBAL = (1 << 28), // NSGlobalBlock

1、block的copy方法流程 (強引用也是copy)

id objc_retainBlock(id x) {
    return (id)_Block_copy(x);
}
void *_Block_copy(const void *arg) {
    struct Block_layout *aBlock;
    
    if (!arg) return NULL;

    aBlock = (struct Block_layout *)arg;
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // 堆上NSConcreteMallocBlock,增加block的引用計數
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        // 全局NSConcreteGlobalBlock,直接返回
        return aBlock;
    }
    else {
        // 棧上NSConcreteStackBlock才支持copy
        // 堆上new一個aBlock的大小--aBlock->descriptor->size
        struct Block_layout *result = malloc(aBlock->descriptor->size);
        if (!result) return NULL;
        
        // 拷貝信息
        memmove(result, aBlock, aBlock->descriptor->size);
        
        // 把堆上的Block的flags改為(BLOCK_HAS_COPY_DISPOSE|BLOCK_NEEDS_FREE|2)
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);
        result->flags |= BLOCK_NEEDS_FREE | 2;
        
        _Block_call_copy_helper(result, aBlock); //--> 最終執行:_Block_object_assign(處理Block截獲的對象,包括正常對象、__block修飾的對象)
        // _Block_call_copy_helper過程理解:編譯代碼中的__main_block_desc_0中的copy方法 >>> __main_block_copy_0 >>> _Block_object_assign
        
        // 把isa指向_NSConcreteMallocBlock
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}
_Block_object_assign (處理block捕獲的對象)
void _Block_object_assign(void *destArg, const void *object, const int flags) {
    const void **dest = (const void **)destArg;
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
        case BLOCK_FIELD_IS_OBJECT:
            /*******
             id object = ...;
             [^{ object; } copy];
             ********/
            
            // 截獲變量是對象:MRC是retain,ARC是空
            _Block_retain_object(object);
            *dest = object;
            break;
            
        case BLOCK_FIELD_IS_BLOCK:
            /*******
             void (^object)(void) = ...;
             [^{ object; } copy];
             ********/
            // 截獲變量是block:Block_copy
            *dest = _Block_copy(object);
            break;
            
        case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
        case BLOCK_FIELD_IS_BYREF:
            /*******
             // copy the onstack __block container to the heap
             // Note this __weak is old GC-weak/MRC-unretained.
             // ARC-style __weak is handled by the copy helper directly.
             __block ... x;
             __weak __block ... x;
             [^{ x; } copy];
             ********/
            
            // 處理Block截獲的__block修飾的變量(對象或者常量,_Block_byref結構體棧上復制到堆上,如果是對象要_Block_object_assign一下,進入下面的 case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT)
            *dest = _Block_byref_copy(object);
            break;

        case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
        case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
            /*******
             // copy the actual field held in the __block container
             // Note this is MRC unretained __block only.
             // ARC retained __block is handled by the copy helper directly.
             __block id object;
             __block void (^object)(void);
             [^{ object; } copy];
             ********/
            // 處理Block_byref結構體中截獲的對象
            *dest = object;
            break;
            
        case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
        case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
            /*******
             // copy the actual field held in the __block container
             // Note this __weak is old GC-weak/MRC-unretained.
             // ARC-style __weak is handled by the copy helper directly.
             __weak __block id object;
             __weak __block void (^object)(void);
             [^{ object; } copy];
             ********/
            
            *dest = object;
            break;
            
        default:
            break;
    }
}

_Block_byref_copy (__block修飾的對象copy,棧上復制到堆上)

static struct Block_byref *_Block_byref_copy(const void *arg) {
    struct Block_byref *src = (struct Block_byref *)arg;
    
    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        
        // 該方法先在堆上生成同樣大小的Block_byref賦值給堆上的Block,
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        // 并把flags設置為src->flags | BLOCK_BYREF_NEEDS_FREE | 4
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        copy->forwarding = copy; // 堆上的forwarding指向自己
        src->forwarding = copy;  // 棧上的forwarding指向堆
        copy->size = src->size;
        
        if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
            // __block修飾對象,這里的意思就是Block_byref要處理這個對象,
            // 執行__Block_byref結構體里面的__Block_byref_id_object_copy_xxx >>> _Block_object_assign,

            struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
            copy2->byref_keep = src2->byref_keep;
            copy2->byref_destroy = src2->byref_destroy;
            
            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                copy3->layout = src3->layout;
            }
            
            (*src2->byref_keep)(copy, src);
        }
        else {
            // Bitwise copy.
            // This copy includes Block_byref_3, if any.
            memmove(copy+1, src+1, src->size - sizeof(*src));
        }
    }
    // 這個Block_byref對象已經在堆上了,引用計數++
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}

__block NSObject *obj = [NSObject new];
Block里面使用obj的地方編譯為:obj.__forwarding->obj

/******* block 修飾對象轉換后的代碼 *******/
/* struct for __block variable */
struct __Block_byref_obj_0 
{
    void *__isa;
    __Block_byref_obj_0 *__forwarding;
    int __flags;
    int __size;
    void (*__Block_byref_id_object_copy)(void*, void*);
    void (*__Block_byref_id_object_dispose)(void*); 
    __strong id obj;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) 
{
    _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) 
{
    _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
/* __block variable declaration */
__Block_byref_obj_0 obj = { 0,
                            &obj,
                            0x2000000, 
                            sizeof(__Block_byref_obj_0), 
                            __Block_byref_id_object_copy_131, 
                            __Block_byref_id_object_dispose_131,
                            [[NSObject alloc] init]
                           };
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_obj_0 *obj; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_obj_0 *_obj, int flags=0) : obj(_obj->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
Block_byref對象:棧-->堆
首先,了解一下內存分配 iOS開發中的內存分配(堆和棧)

(1)棧(stack):由編譯器自動分配釋放,存放方法的參數值,局部變量等。棧是向低地址括展的數據結構,是連續的區域。棧頂的地址和棧的最大容量是系統預先規定好的。
優點:快速高效,
缺點:容量有限制
靜態分配:靜態分配是編譯器完成的,比如局部變量的分配。
動態分配:動態分配由alloca函數進行分配,由編譯器進行釋放,無需我們手工實現。
(2)堆(heap):程序員分配釋放,向高地址括展的數據結構,是不連續的區域。
(3)全局區(靜態區)(static):全局變量和靜態變量的存儲,分初始化和未初始化bss(data、bss 兩塊區域),程序結束系統釋放。
(4)常量區:字符串常量,const常量,程序結束系統釋放。
(5)代碼區:存放函數體的二進制代碼。也就是說是它是可執行程序在內存種的鏡像。代碼段需要防止在運行時被非法修改,所以只準許讀取操作,而不允許寫入(修改)操作——它是不可寫的。

看代碼

int age = 24;//全局初始化區(數據區)
NSString *name;//全局未初始化區(BSS區)
static NSString *sName = @"Dely";//指針sName的地址在全局(靜態初始化)區,"Dely"本身在常量去,字符串常量

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    int tmpAge;//棧
    NSString *tmpName = @"Dely";  //指針tmpName的地址在棧區,"Dely"本身在常量去,字符串常量
    NSString *number = @"123456"; //指針number的地址在棧上,123456 在常量區,
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];//分配而來的8字節的區域就在堆中,指針array在棧中,指向堆區的地址
    NSInteger total = [self getTotalNumber:1 number2:1];

}

- (NSInteger)getTotalNumber:(NSInteger)number1 number2:(NSInteger)number2{
    return number1 + number2;//number1和number2 棧區
}
1、局部變量
(1)無_block修飾:

基本類型

    int a = 10;
    NSLog(@"block定義前a地址=%p", &a);
    void (^aBlock)() = ^(){
        NSLog(@"block定義內部a地址=%p", &a);
    };
    NSLog(@"block定義后a地址=%p", &a);
    aBlock();
    
    /*
     結果:
     block定義前a地址=0x7fff5bdcea8c
     block定義后a地址=0x7fff5bdcea8c
     block定義內部a地址=0x7fa87150b850
     */
    
    /*
     流程:
     1. block定義前:a在棧區
     2. block定義內部:里面的a是根據外面的a拷貝到堆中的,已經不是同一個a了,
     3. block定義后:a在棧區
     */

指針類型:(block捕獲變量,相當于在block結構體中開一個同名的變量,指向局部變量的對象,被block持有,引用數+1)
(如果block里面重新賦值,也是block.a改變了,與block外面的變量a沒有關系,賦值沒有任何意義,所以蘋果禁止賦值,xcode提示不允許。這就是為什么沒有__block修飾的變量不能賦值原因。)

    NSObject *obj = [[NSObject alloc] init]; // 局部變量--指針變量,多提一下:“=”賦值右邊的(alloc init)分配的地址是在堆上,左邊obj指針地址是在棧上
    NSLog(@"block定義前:[obj本身的地址=%p,obj指向的地址=%p]",&obj,obj);
    void (^aBlock)() = ^(){
        NSLog(@"block定義內部:[obj本身的地址=%p,obj指向的地址=%p]",&obj,obj);
    };
    NSLog(@"block定義后:[obj本身的地址=%p,obj指向的地址=%p]",&obj,obj);
    aBlock();

    /*
     NSLog輸出:
     block定義前:[obj本身的地址=0x7fff5bebed30,obj指向的地址=0x60000001f750]
     block定義后:[obj本身的地址=0x7fff5bebed30,obj指向的地址=0x60000001f750]
     block定義內部:[obj本身的地址=0x600000059910,obj指向的地址=0x60000001f750]
     */
    
    /*
     解析:
     1. block定義前:指針obj本身的地址在棧區,指針obj指向的對象是在堆上
     2. block定義內部:在block結構體中(block在堆上)開一個同名的變量(堆上),指向obj指向的對象,被block持有,引用數+1
     3. block定義后:obj沒有變化。
     */
(2)有_block修飾

基本數據類型

    __block int b = 10;
    NSLog(@"block定義前b地址=%p", &b);
    void (^bBlock)() = ^(){
        b = 20;
        NSLog(@"block定義內b地址=%p", &b);
    };
    NSLog(@"block定義后b地址=%p", &b);
    bBlock();
    
    /*
     結果:
    block定義前b地址=0x7fff5c446c28
    block定義后b地址=0x60400042b318
    block定義內b地址=0x60400042b318
     */

指針類型:

    __block NSString *obj = [[NSString alloc] initWithFormat:@"111"]; // 局部變量--指針變量,多提一下:“=”賦值右邊的(alloc init)分配的地址是在堆上,左邊obj指針地址是在棧上
    NSLog(@"block定義前:[obj本身的地址=%p,obj指向的地址=%p]",&obj,obj);
    void (^aBlock)() = ^(){
        NSLog(@"block定義內:[obj本身的地址=%p,obj指向的地址=%p]",&obj,obj);
    };
    NSLog(@"block定義后:[obj本身的地址=%p,obj指向的地址=%p]",&obj,obj);
    aBlock();
    
    /*
     結果:
     block定義前:[obj本身的地址=0x7fff5d32fd38,obj指向的地址=0xa000000003131313]
     block定義后:[obj本身的地址=0x600000052298,obj指向的地址=0xa000000003131313]
     block定義內:[obj本身的地址=0x600000052298,obj指向的地址=0xa000000003131313]
     */

2、全局變量或者全局靜態變量:因為都是在全局區,在程序結束前不會被銷毀

block內部不需要處理全局變量或者全局靜態變,直接使用

    //所以block直接訪問了對應的變量(沒有對變量處理)
    int global_var = 100;
    static int static_global_var = 200;
    void(^globalVarBlock)() = ^{
        
        NSInteger var = global_var; // 可以使用
        global_var = 1000; // 可以直接訪問或者賦值
        static_global_var = 10000; // 可以直接訪問或者賦值
        
    };
3、局部靜態變量:將靜態變量的指針傳遞給block,block通過靜態局部變量的地址來進行訪問

(__block(__Block_byref_XXX_n)結構體也是通過指針傳遞給block內部,所以MRC使用__block可以預防循環引用,且可以重新賦值修改__block修飾的變量)

{
    static NSInteger staticLocalVar = 10; 
    void(^staticLocalVarBlock)() = ^{
        
        NSInteger bvar = staticLocalVar;//可以使用
        staticLocalVar = 1000;//可以使用
        
    };
}

block的@property

ARC會自動幫strong類型且捕獲外部變量的block進行copy,所以在定義block類型的屬性時也可以使用strong,不一定使用copy

    // 假如有棧block賦給以下兩個屬性
    // ARC,當棧block中會捕獲外部變量時, 這個block會被copy進堆中
    // 如果沒有捕獲外部變量,這個block會變為全局類型
    // 不管怎么樣,它都脫離了棧生命周期的約束
    @property (nonatomic,strong) Block *strongBlock;
    @property (nonatomic,copy) Block *copyBlock;

四、block使用中出現retain cycle的問題、解決辦法

最簡單的循環問題代碼

self.myBlock = ^{
        NSLog(@"=====%@",self);//這種明顯的循環引用,xcode會提示。
    };

解決辦法:(常用)

    __weak __typeof(self)weakSelf = self;
    self.myBlock = ^{
        __strong __typeof(weakSelf)strongSelf = weakSelf;;//這個作用是防止block執行過程中self釋放導致block執行了一半就GG。
        NSLog(@"=====%@", strongSelf);
    };

解決辦法:(不常用)
Block 多開一個參數,傳入self,然后使用self的一些屬性方法,這樣子不會retain cycle

介紹做法:
為 block 多加一個參數,也就是 self 所屬類型的參數,那么在 block 內部,該參數就會和 strongSelf 的效果一致。同時你也可以不寫 weakSelf,直接使用使用該參數(作用等同于直接使用 strongSelf )。這樣就達到了:“多加一個參數,省掉兩行代碼”的效果。原理就是利用了“參數”的特性:參數是存放在棧中的(或寄存器中),系統負責回收,開發者無需關心。因為解決問題的思路是:將 block 會捕獲變量到堆上的問題,化解為了:變量會被分配到棧(或寄存器中)上,所以我把種做法起名叫 Heap-Stack Dance 。
引用來自--iOS程序猿 使用 Heap-Stack Dance 替代 Weak-Strong Dance,優雅避開循環引用。

#import "Foo.h"
typedef void (^Completion)(Foo *foo);
@interface Foo ()
@property (nonatomic, copy) Completion completion1;
@end

@implementation Foo
- (instancetype)init {
    if (!(self = [super init])) {
        return nil;
    }
    self.completion2 = ^(Foo *foo) {
        //使用foo的各種屬性、方法
    };
    self.completion2(self);
    return self;
}
- (void)dealloc {
    NSLog(@"dealloc");//這能執行
}
@end

五、block的思考與實際使用的疑問,為什么有些系統SDK或者第三方庫API中有block的使用中會引發循環引用,有些不會?

(1) GCD不會 (內部是copy這個block)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // something
    NSLog(@"=====%@",self.view);
});
(2) presentViewController不會 (這個block沒有被任何對象持有,只是傳進去執行)
UIViewController *vc = [[UIViewController alloc] init];
[self presentViewController:vc animated:YES completion:^{
    NSLog(@"=====%@",self.view);
}];
(3) AFNetWork
//self -> manager(AFHTTPSessionManager)->mutableTaskDelegatesKeyedByTaskIdentifier->delegate->block->self
//AFNetwork 在請求結束后會自動釋放掉 delegate,打破那個環,所以循環引用也就不存在了.
manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.hao123.com"]];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    NSLog(@"=====%@",self.view);
}];
[dataTask resume];
(4) SDWebImage
//slef -> view- -> imageView -> operationDictionary -> SDWebImageOperation -> OperationCompletedBlock  -> imageViewCompletedBlock -> self
//跟AFNetwork一樣,等待operation請求完成自動remove,斷開連接
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.view addSubview:imageView];//self.view會retain imageView。
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://img17.3lian.com/d/file/201702/18/3e8536054dd699a2134f86c200f7c079.jpg"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
    NSLog(@"=====%@",self.view);
}];

AFNetwork、SDWebImage都是要等請求結束才能斷開retain cycle,self得不到快速釋放。假設請求時間100年,這個self就100年得不到釋放。只是假設,哈哈

(5) 其他方式出現的retain cycle,NSNotification、NSTimer、自己寫的delegate都需要好好注意一下!!!!
[[NSNotificationCenter defaultCenter] addObserverForName:@"SecondViewController" object:nil queue:NULL usingBlock:^(NSNotification *note) {
}];
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {}];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容