Block底層原理四-循環(huán)引用分析

循環(huán)引用非常常見,我們來分析一下為什么會循環(huán)引用


#import <Foundation/Foundation.h>
#import "WKPerson.h"

typedef void (^WKBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        WKPerson *person = [[WKPerson alloc] init];
        person.age = 20;
        person.block = ^{
            
        };
    }
     NSLog(@"---------");
    return 0;
}

//WKPerson.h
#import <Foundation/Foundation.h>

typedef void (^WKBlock) (void);

@interface WKPerson : NSObject

@property (copy, nonatomic) WKBlock block;
@property (assign, nonatomic) int age;


@end

//WKPerson.m
#import "WKPerson.h"

@implementation WKPerson

- (void)dealloc
{
    //    [super dealloc];
    NSLog(@"%s", __func__);
}

@end

2018-08-21 10:48:02.483714+0800 mhhhh[16581:274772] -[WKPerson dealloc]
2018-08-21 10:48:02.484591+0800 mhhhh[16581:274772] ---------
Program ended with exit code: 0

  • 我們看到了WKPerson已經(jīng)被釋放掉了dealloc方法執(zhí)行了
  • 接下來我要加一句代碼,就是這句代碼導(dǎo)致循環(huán)引用
#import <Foundation/Foundation.h>
#import "WKPerson.h"

typedef void (^WKBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        WKPerson *person = [[WKPerson alloc] init];
        person.age = 20;
        person.block = ^{
            NSLog(@"%d",person.age);
        };
    }
     NSLog(@"---------");
    return 0;
}

2018-08-21 10:52:00.748869+0800 mhhhh[16622:281567] ---------
Program ended with exit code: 0

我們來分析一下 為什么加了NSLog后會循環(huán)引用

 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
//main.m
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  WKPerson *person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, WKPerson *_person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

我們來看看WKPerson類

 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc WKPerson.m
struct WKPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    WKBlock _block;
};

struct NSObject_IMPL {
    Class isa;
};

粗略的說 NSObject內(nèi)部

struct objc_class{
      Class isa;
      Class superclass;
      cache_t cache;//方法緩存
      class_data_bits_t bits;//用于具體方法信息
}

struct class_rw_t{
        uint32_t flags;
        const class_ro_t *ro;
        property_list_t *properties //屬性列表
}

class_ro_t 屬性列表就放著instance 成員變量列表也就是age屬性
后面我會專門寫一篇NSObject里面的內(nèi)存 以及isa的指向

接下來我們分析一下 為啥會循環(huán)引用
1.person->block 調(diào)用了block
2.block內(nèi)部調(diào)用了person.age
3.person isa存著block的內(nèi)存地址,這個內(nèi)存地址就是person->block
4.所以當(dāng)person作用域結(jié)束了,還有一根強(qiáng)指針指向他,person不能釋放

圖花的比較丑


image.png

如果這時候加上__weak

  • person->block 調(diào)用了block(強(qiáng)指針)
  • block內(nèi)部調(diào)用了person.age
  • person isa存著block的內(nèi)存地址,這個內(nèi)存地址就是person->block (weak)弱指針,也就是下面的那根線變成了虛線,所以當(dāng)persion作用域結(jié)束后,就會調(diào)用dispose函數(shù),進(jìn)行釋放,釋放后引用計數(shù)器-1等于0 所以就能夠釋放。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容