關于block的所有問題

關于block捕獲外部的變量

(1)局部變量
(2)靜態(tài)變量
(3)全局變量
(4)全局靜態(tài)變量
(6)block捕獲外邊的變量在里面可以更改變量的值嗎?可更改是為什么,不可更改是為什么.
(5)block捕獲基本類型變量和捕獲對象類型變量的區(qū)別

關于block的類型

(1)堆block
(2)棧block
(3)全局block
(4)block在什么情況下分別是什么類型

關于block在MRC和在ARC注意事項

(1)block之后copy 分別在MRC, ARC會發(fā)生什么
(2)block會不會持有捕獲的對象變量

關于循環(huán)引用__weak

(1)__weak對象或者__strong對象,block是怎么對捕獲的對象進行內存管理的

關于__block

(1)__block修飾后的變量本質是什么
(2)在block內部為什么不能修改外邊的局部變量

我們先看看下面代碼根據(jù)xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m命令轉成的c++代碼

int globle_a = 1;
static int globlea_static_a = 2;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autob = 1;
        auto int autoa = 1;
        static int statica = 1;
        void(^lc_block)(int , NSObject *) = ^(int a, NSObject *obj){
            //參數(shù)
            NSLog(@"%d",a);
            NSLog(@"%@",obj);
            
            //捕獲的外部變量
            NSLog(@"%d",autoa);
            NSLog(@"%d",statica);
            NSLog(@"%d",globle_a);
            NSLog(@"%d",globlea_static_a);
        };
       
        lc_block(10,[NSObject new]);
    }
    return 0;
}

下面是main.cpp中關于block 的關鍵代碼

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

int globle_a = 1;
static int globlea_static_a = 2;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int autoa;
  int *statica;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _autoa, int *_statica, int flags=0) : autoa(_autoa), statica(_statica) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, NSObject *obj) {
  int autoa = __cself->autoa; // bound by copy
  int *statica = __cself->statica; // bound by cop
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_0,a);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_1,obj);


            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_2,autoa);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_3,(*statica));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_4,globle_a);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_93c220_mi_5,globlea_static_a);
        }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        auto int autob = 1;
        auto int autoa = 1;
        static int statica = 1;
        void(*lc_block)(int , NSObject *) = ((void (*)(int, NSObject *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica));

        ((void (*)(__block_impl *, int, NSObject *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block, 10, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")));
    }
    return 0;
}
void(*lc_block)(int , NSObject *) = ((void (*)(int, NSObject *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica));
這行代碼
可以簡化成這樣 去掉一些強制烈性裝換
lc_block = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica)

__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, autoa, &statica)是__main_block_impl_0C++結構體的構造函數(shù),可以理解成OC的初始化方法

根據(jù)上面的解釋我們可以看出lc_block其實就是指向__main_block_impl_0結構體的指針到這里我們其實已經可以看到lc_block是怎么捕獲外邊的變量了__main_block_impl_0構造函數(shù)的 第一個參數(shù)(void)__main_block_func_0 就是block的實現(xiàn)函數(shù)指針, 第二個參數(shù)&__main_block_desc_0_DATA暫時先不做解釋,第三,第四個參數(shù)autoa, &statica就是我們捕獲外邊的變量 ,說道這里我們至少明白了一個問題
(1)block是怎么捕獲外邊的變量的.
下面我們回答一下這兩個問題 block是在__main_block_impl_0初始化的時候把需要的捕獲的變量令作為參數(shù)傳進__main_block_impl_0結構體的

下面我們再來分析一下block的調用

((void (*)(__block_impl *, int, NSObject *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block, 10, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new")))
去掉一些類型強制轉換其實可以變成下面這樣
lc_block->FuncPtr(lc_block, 10,[NSObject new]);

impl.FuncPtr = fp; FuncPtr就是我們在初始化結構體穿進去的第一個參數(shù)就是__main_block_func_0函數(shù)的指針,這里大家可能有個疑問按照正常的寫法不應該是lc_block->impl.FuncPtr(lc_block, 10,[NSObject new]);這樣才對嘛. 是的這樣是沒錯,但是直接lc_block->FuncPtr加上類型強制轉換也是沒有問題的原因大概解釋一下因為impl是結構體中第一個成員變量所以lc_block指針指向的其實是impl的地址.
lc_block->FuncPtr(lc_block, 10,[NSObject new]);就是調用了__main_block_func_0這個函數(shù)(再次強調一下這個函數(shù)是block的實現(xiàn))這個函數(shù)的第一個參數(shù)就是block的指針.第二個第三個參數(shù)是我們自己傳進去的.
看到這里我們可以基本可以回答兩個問題.

(1)block捕獲會捕獲外邊的什么變量

下面我們回答一下這兩個問題block里面用到的變量block才會捕獲但是block不會捕獲 全局變量, static靜態(tài)變量(可以??一下為啥block不會捕獲全局變量,全局靜態(tài)變量) 細心的朋友可能還發(fā)現(xiàn)了block捕獲局部變量autoa是值傳遞,捕獲局部靜態(tài)變量statica是指針傳遞.這也就說明為啥block中可以修改靜態(tài)變量不可以修改局部變量了(可以??一下為啥block不會捕獲局部變量為啥是值傳遞,static變量為啥是指針傳遞)

下面我們分析block的類型 我們先分析在MAC情況下

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
        };
        
        NSLog(@"%@",[lcc_block class]);
        [lcc_block release];
    }
    return 0;
}
2020-01-04 21:29:07.556789+0800 block_1[64578:2231805] __NSGlobalBlock__

int globle_a = 1;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
            NSLog(@"%d",globle_a);
        };
        
        NSLog(@"%@",[lcc_block class]);
        [lcc_block release];
    }
    return 0;
}
2020-01-04 21:29:07.556789+0800 block_1[64578:2231805] __NSGlobalBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        static int statica = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
            NSLog(@"%d",statica);
        };
        
        NSLog(@"%@",[lcc_block class]);
        [lcc_block release];
    }
    return 0;
}
2020-01-04 21:32:02.108197+0800 block_1[64603:2235019] __NSGlobalBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        static int statica = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
            NSLog(@"%d",autoa);
        };
        
        NSLog(@"%@",[lcc_block class]);
        [lcc_block release];
    }
    return 0;
}
2020-01-04 21:33:35.248612+0800 block_1[64630:2237066] __NSStackBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        static int statica = 1;
        void(^lcc_block)(void) =  [^(void){
            //捕獲的外部變量
            NSLog(@"%d",autoa);
        } copy];
        
        NSLog(@"%@",[lcc_block class]);
        [lcc_block release];
    }
    return 0;
}
2020-01-04 21:34:23.187194+0800 block_1[64644:2238310] __NSMallocBlock__

我們總結一下結論

block類型 環(huán)境
NSGlobalBlock 沒有訪問auto變量
NSStackBlock 訪問了auto變量
NSMallocBlock NSStackBlock調用了copy

我們再分析ARC的情況

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
        };
        
        NSLog(@"%@",[lcc_block class]);
    }
    return 0;
}
2020-01-04 21:42:00.940409+0800 block_1[64708:2245501] __NSGlobalBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
            NSLog(@"%d",globle_a);
        };
        
        NSLog(@"%@",[lcc_block class]);
    }
    return 0;
}
2020-01-04 21:42:56.328330+0800 block_1[64721:2246791] __NSGlobalBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        static int statica = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
            NSLog(@"%d",statica);
        };
        
        NSLog(@"%@",[lcc_block class]);
       
    }
    return 0;
}
2020-01-04 21:44:54.807266+0800 block_1[64745:2248664] __NSGlobalBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        static int statica = 1;
        void(^lcc_block)(void) =  ^(void){
            //捕獲的外部變量
            NSLog(@"%d",autoa);
        };
        
        NSLog(@"%@",[lcc_block class]);
       
    }
    return 0;
}
2020-01-04 21:45:31.803956+0800 block_1[64756:2249690] __NSMallocBlock__

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        static int statica = 1;
        void(^lcc_block)(void) =  [^(void){
            //捕獲的外部變量
            NSLog(@"%d",autoa);
        } copy];
        
        NSLog(@"%@",[lcc_block class]);
        
    }
    return 0;
}
2020-01-04 21:46:55.321199+0800 block_1[64767:2251090] __NSMallocBlock__

我們可以看出在MRC和在ARC的情況下不同的是block訪問auto局部變量是NSMallocBlock 難道是我們以上總結的結論是錯的嗎? 答案是不是.

看下面代碼

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int autoa = 1;
        
        NSLog(@"%@",[^(void){NSLog(@"%d",autoa);} class]);
        
    }
    return 0;
}
2020-01-04 21:58:45.531138+0800 block_1[64814:2259295] __NSStackBlock__

在ARC的情況下這樣的block還是NSStackBlock和我們上表格總結的訪問了auto變量 是NSStackBlock并不矛盾

在ARC的情況下編譯器在什么情況下會自動 將NSStackBlock調用了copy轉成NSMallocBlock呢?
block作為函數(shù)返回值時
將block賦值給__strong指針時
block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
block作為GCD API的方法參數(shù)時

不知道大家有沒有思考過這個問題編譯器為什么要把棧block copy一下變成堆block, 這樣的好處是啥?
帶著疑問我們看下面的代碼

//在MRC的情況下
void(^lc_blcok)(void);
void lc_test(){
    auto int autoa = 1;
    lc_blcok = ^(void){
        NSLog(@"autoa = %d",autoa);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        lc_test();
        lc_blcok();
    }
    return 0;
}
2020-01-04 22:15:13.271055+0800 block_1[64958:2273028] __NSStackBlock__
2020-01-04 22:15:13.271751+0800 block_1[64958:2273028] autoa = -27

我們發(fā)現(xiàn)打印的autoa這個變量是個奇怪的值原因是lc_blcok是NSStackBlock雖然我們用一個全局指針lc_blcok指向這個block(也可以說是結構體 因為我們通過前面的代碼分析出來block其實就是一個結構體的指針)但是這個結構體是在棧 上呢,在棧上的數(shù)據(jù)有個特點就是他的內存管理是系統(tǒng)控制的, lc_test函數(shù)以調用完畢內存就可呢釋放了 所以block捕獲進去的autoa也是垃圾內存了. 所以編譯器會根據(jù)情況自動將棧上的block復制到堆上.

說道這里還能引申一個問題就是在block作為一個類的屬性時到底是用strong 修飾還是用copy修飾呢
結論是
ARC的情況下 用strong和copy 都可以
MRC情況下建議用copy

原因相信聰明的你已經知道了

block捕獲對象類型是否持有auto對象
下面我們根據(jù) clang命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m再看看轉成c++的代碼 (我們考慮到有__weak的情況現(xiàn)在只討論arc)

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LCTest1 *testObj = [[LCTest1 alloc]init];
        testObj.age = 10;
        
        
        LCTest1 *testObj2 = [[LCTest1 alloc]init];
        testObj2.age = 10;
        __weak LCTest1 *weakTestObj = testObj2;
        
        void(^lc_block)(void) =  ^(void){
            NSLog(@"%@",testObj);
            NSLog(@"%@",weakTestObj);
        };
        
        lc_block();
    }
    return 0;
}

下面是轉成c++的關鍵代碼

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  LCTest1 *__strong testObj;
  LCTest1 *__weak weakTestObj;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj, LCTest1 *__weak _weakTestObj, int flags=0) : testObj(_testObj), weakTestObj(_weakTestObj) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  LCTest1 *__strong testObj = __cself->testObj; // bound by copy
  LCTest1 *__weak weakTestObj = __cself->weakTestObj; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_ba54bf_mi_0,testObj);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_ba54bf_mi_1,weakTestObj);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakTestObj, (void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        LCTest1 *testObj = ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"));
        ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)testObj, sel_registerName("setAge:"), 10);


        LCTest1 *testObj2 = ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"));
        ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)testObj2, sel_registerName("setAge:"), 10);
        __attribute__((objc_ownership(weak))) LCTest1 *weakTestObj = testObj2;

        void(*lc_block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, testObj, weakTestObj, 570425344));

        ((void (*)(__block_impl *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block);
    }
    return 0;
}

我們可以看出block在捕獲外面auto變量和捕獲基本類型的變量是有所不同的.

我們看__main_block_desc_0這個結構體里面多了兩個函數(shù)指針變量
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);

下面就是函數(shù)指針變量的實現(xiàn) __main_block_copy_0函數(shù)會在block從棧被拷貝到堆上自動調用,
__main_block_dispose_0函數(shù)會在堆上的block被銷毀的時候自動調用

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakTestObj, (void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakTestObj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

我們看到 __main_block_impl_0結構體中捕獲的 testObj的變量和weakTestObj變量 分別用__strong, __weak修飾
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  LCTest1 *__strong testObj;
  LCTest1 *__weak weakTestObj;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj, LCTest1 *__weak _weakTestObj, int flags=0) : testObj(_testObj), weakTestObj(_weakTestObj) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

看到這里我們可能已經能夠感覺到了block捕獲外面的auto對象變量會通過__main_block_copy_0里面的_Block_object_assign函數(shù)和__main_block_dispose_0里面的_Block_object_dispose函數(shù)對auto對象變量做內存方面的管理 那我們是不是人為 只要是__strong修飾的對象block就會持有(是對象的引用計數(shù)+1)這個對象__weak修飾的對象就不會持有這個對象呢? 這個結論不太準確 我們舉個例子

image.png

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  LCTest1 *testObj;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *_testObj, int flags=0) : testObj(_testObj) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
可以看出 __main_block_impl_0中的捕獲了testObj但是在61行testObj就被釋放了所以block中并內有持有testObj

我們再看一個arc下的例子

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        void(^lc_block)(void) = nil;
        
        testObj1 = nil;
        autoa++;
        
         LCTest1 *testObj1 = [[LCTest1 alloc]init];
        testObj1.age = 10;
    
        ^(void){
            NSLog(@"%@",testObj1);
        };
        
        printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(testObj1)));
        
    }
    return 0;
}

testObj retain count = 1可以看出block也沒有引用testObj
我們看下轉換過之后的 c++關鍵代碼 我們發(fā)現(xiàn)
雖然LCTest1 *__strong testObj1 是__strong但是testObj并沒有被block強持有

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  LCTest1 *__strong testObj1;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, LCTest1 *__strong _testObj1, int flags=0) : testObj1(_testObj1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

經過上面的分析我們 我們看到__strong修飾的變量,block并不一定會強引用這個__strong變量..
下面給出總結
如果block是在棧上,將不會對auto變量產生強引用
如果block被拷貝到堆上會調用block內部的copy函數(shù)
copy函數(shù)內部會調用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong、__weak砚殿、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用

下面我們再分析一下__block

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block LCTest1 *testObj1 = [[LCTest1 alloc]init];
        
        __block int autoa = 1;
        
        void(^lc_block)(void) = ^(void){
            NSLog(@"%@",testObj1);
            NSLog(@"%d",autoa);
        };
        
        lc_block();
        
        printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(testObj1)));
        
    }
    return 0;
}

下面是轉換成c++的代碼

struct __Block_byref_testObj1_0 {
  void *__isa;
__Block_byref_testObj1_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 LCTest1 *__strong testObj1;
};
struct __Block_byref_autoa_1 {
  void *__isa;
__Block_byref_autoa_1 *__forwarding;
 int __flags;
 int __size;
 int autoa;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_testObj1_0 *testObj1; // by ref
  __Block_byref_autoa_1 *autoa; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_testObj1_0 *_testObj1, __Block_byref_autoa_1 *_autoa, int flags=0) : testObj1(_testObj1->__forwarding), autoa(_autoa->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_testObj1_0 *testObj1 = __cself->testObj1; // bound by ref
  __Block_byref_autoa_1 *autoa = __cself->autoa; // bound by ref

            (testObj1->__forwarding->testObj1) = __null;
            (autoa->__forwarding->autoa)++;
            
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_d1c897_mi_0,(testObj1->__forwarding->testObj1));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_fsnlrf813993bz18v7qy__0h0000gn_T_main_d1c897_mi_1,(autoa->__forwarding->autoa));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testObj1, (void*)src->testObj1, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->autoa, (void*)src->autoa, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testObj1, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->autoa, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_testObj1_0 testObj1 = {
            (void*)0,
            (__Block_byref_testObj1_0 *)&testObj1,
            33554432,
            sizeof(__Block_byref_testObj1_0),
            __Block_byref_id_object_copy_131,
            __Block_byref_id_object_dispose_131,
            ((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)((LCTest1 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LCTest1"), sel_registerName("alloc")), sel_registerName("init"))
        };

        __attribute__((__blocks__(byref))) __Block_byref_autoa_1 autoa = {
            (void*)0,
            (__Block_byref_autoa_1 *)&autoa,
            0,
            sizeof(__Block_byref_autoa_1),
            1
        };

        void(*lc_block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_testObj1_0 *)&testObj1, (__Block_byref_autoa_1 *)&autoa, 570425344));

        ((void (*)(__block_impl *))((__block_impl *)lc_block)->FuncPtr)((__block_impl *)lc_block);

        printf("testObj retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)((testObj1.__forwarding->testObj1))));

    }
    return 0;
}

可以看出__block修飾的變量在最終會被包裝成一個結構體如果是__block修飾的是對象變量這個結構體中會有兩個函數(shù)指針__Block_byref_id_object_copy,和__Block_byref_id_object_dispose分別對對象變量做內存方面的管理,結構體里面有個重要的指針__forwarding是指想自己的指針.
我們從block的實現(xiàn)中__main_block_func_0看到__Block_byref_testObj1_0 *testObj1 = __cself->testObj1; // bound by ref
__Block_byref_autoa_1 *autoa = __cself->autoa; // bound by ref
testObj1和autoa分別是包裝成block的結構體的指針修改__block對象其實是通過
包裝成block的指針 拿到 __forwarding(包裝成block的指針)再拿到真正的變量然后修改

現(xiàn)在大家可能有一個疑問為啥不通過包裝成block的指針直接拿到真正的變量
(testObj1->__forwarding->testObj1) = __null;
(autoa->__forwarding->autoa)++;

image.png

image.png

我們根據(jù)上面的圖可以看出當block在棧上包裝__block修飾的變量的結構體中__forwarding是指向自己,當block通過copy拷貝到堆上包裝__block修飾的變量的結構體中__forwarding指向的就是堆上的, 猜測難道是我們再__main_block_func_0這個函數(shù)中使用的__Block_byref_testObj1_0 *testObj1和__Block_byref_autoa_1 *autoa 是棧上的? 如果是testObj1和autoa 是棧上的那么__forwarding指針就是上圖說的意義了. 如果是堆上的__forwarding指針貌似沒有存在的意義,因為完全可以通過下面這樣修改堆上的結果,沒必要通過__forwarding再轉一下
(堆上的)
testObj1-> testObj1
(堆上的)
autoa-> autoa

我們可以用代碼驗證一下

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __Block_byref_obj2_0 {
  void *__isa;
 struct __Block_byref_obj2_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *obj2;
};

struct __Block_byref_autoa_1 {

    void *__isa;
 struct __Block_byref_autoa_1 *__forwarding;
 int __flags;
 int __size;
 int autoa;
};



struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
    
  struct __Block_byref_obj2_0 *obj2; // by ref
  struct __Block_byref_autoa_1 *autoa; // by ref
};

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
};



int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 1;
        NSObject *obj1 = [[NSObject alloc]init];
        
        __block NSObject *obj2 = [[NSObject alloc]init];
        __block int autoa = 10;
        void(^lc_block)(void) = [^(void){
            obj2 = nil;
            autoa++;
        } copy];
        
        struct __main_block_impl_0 * Static_block = (__bridge struct __main_block_impl_0 *)((id)lc_block);
        NSLog(@"lc_block class = %@",[lc_block class]);
        lc_block();
    }
    return 0;
}

我們在lc_block();這一行代碼打上斷點
我們通過 po查看


2020-01-07 11:30:43.578077+0800 block_1[98305:3389230] lc_block class = __NSMallocBlock__
(lldb) po &a
0x00007ffeefbff54c

(lldb) po obj1
<NSObject: 0x1006758f0>

(lldb) po obj2
<NSObject: 0x100675930>

(lldb) po Static_block
<__NSMallocBlock__: 0x1006785b0>
 signature: "v8@?0"
 invoke   : 0x100000da0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__main_block_invoke)
 copy     : 0x100000de0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__copy_helper_block_e8_32r40r)
 dispose  : 0x100000e40 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__destroy_helper_block_e8_32r40r)

(lldb) po Static_block->obj2
0x00000001006785e0

(lldb) po Static_block->obj2->__forwarding
0x00000001006785e0

(lldb) po Static_block->obj2->__forwarding->obj2
<NSObject: 0x100675930>

(lldb) po &autoa
0x0000000100678628

(lldb) po Static_block->autoa
0x0000000100678610

(lldb) po Static_block->autoa->__forwarding
0x0000000100678610

(lldb) po &(Static_block->autoa->__forwarding->autoa)
0x0000000100678628

(lldb) 


我們分析一下 
&a 0x00007ffeefbff54c 這個肯定是棧地址
po obj1 <NSObject: 0x1006758f0> 這個肯定是堆地址

po Static_block 0x1006785b0 這個地址和obj1接近也可以看到到block的類型 卻是是在堆

Static_block->obj2 0x00000001006785e0 這個地址也是堆

po &autoa  0x0000000100678628 我們看這個地址也是堆
可以看出 基本類型本來應該在棧上 經過__block修飾可以看出被分到了堆上

po Static_block->autoa 0x0000000100678610這個地址也是在堆上,可以看出我們直接用的就是堆上并不是棧上的 那么有必要 autoa-> __forwarding-> autoa  我想不通__forwarding存在的意義

==================================================
我們在分析一下棧上的block
#import <Foundation/Foundation.h>
#import "LCTest1.h"

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __Block_byref_obj2_0 {
  void *__isa;
 struct __Block_byref_obj2_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *obj2;
};

struct __Block_byref_autoa_1 {

    void *__isa;
 struct __Block_byref_autoa_1 *__forwarding;
 int __flags;
 int __size;
 int autoa;
};



struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
    
  struct __Block_byref_obj2_0 *obj2; // by ref
  struct __Block_byref_autoa_1 *autoa; // by ref
};

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
};



int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 1;
        NSObject *obj1 = [[NSObject alloc]init];
        
        __block NSObject *obj2 = [[NSObject alloc]init];
        __block int autoa = 10;
        void(^lc_block)(void) = ^(void){
            obj2 = nil;
            autoa++;
        };
        
        struct __main_block_impl_0 * Static_block = (__bridge struct __main_block_impl_0 *)((id)lc_block);
        NSLog(@"lc_block class = %@",[lc_block class]);
        lc_block();
    }
    return 0;
}


2020-01-07 11:05:15.894243+0800 block_1[98239:3381416] lc_block class = __NSStackBlock__
(lldb) po &a
0x00007ffeefbff54c

(lldb) po obj1
<NSObject: 0x1006355b0>

(lldb) po Static_block
<__NSStackBlock__: 0x7ffeefbff4b8>
 signature: "v8@?0"
 invoke   : 0x100000da0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__main_block_invoke)
 copy     : 0x100000de0 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__copy_helper_block_e8_32r40r)
 dispose  : 0x100000e40 (/Users/lcc/Library/Developer/Xcode/DerivedData/block_1-heucsvushdyhgchdcamixyeeosnt/Build/Products/Debug/block_1`__destroy_helper_block_e8_32r40r)

(lldb) po Static_block->obj2
0x00007ffeefbff510

(lldb) po Static_block->obj2->__forwarding
0x00007ffeefbff510

(lldb) po Static_block->obj2->__forwarding->obj2
<NSObject: 0x100635920>

(lldb) po obj2
<NSObject: 0x100635920>

(lldb) po &autoa
0x00007ffeefbff508

(lldb) po Static_block->autoa
0x00007ffeefbff4f0

(lldb) po Static_block->autoa->__forwarding
0x00007ffeefbff4f0

(lldb) po &(Static_block->autoa->__forwarding->autoa)
0x00007ffeefbff508

po  Static_block->obj2 0x00007ffeefbff510 是在棧上

po Static_block->obj2->__forwarding 0x00007ffeefbff510還是在棧上

po Static_block->autoa  0x00007ffeefbff4f0 是在棧上

po Static_block->autoa->__forwarding 0x00007ffeefbff4f0 還是在棧上

__forwarding也是沒有存在的意義啊 

經過我的分析我還是沒有弄明白__forwarding存在的意義,哪位大佬了解拜托指教一二

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末誊涯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌欧募,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹰祸,死亡現(xiàn)場離奇詭異甫窟,居然都是意外死亡,警方通過查閱死者的電腦和手機蛙婴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門粗井,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人街图,你說我怎么就攤上這事浇衬。” “怎么了餐济?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵耘擂,是天一觀的道長。 經常有香客問我絮姆,道長梳星,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任滚朵,我火速辦了婚禮冤灾,結果婚禮上,老公的妹妹穿的比我還像新娘辕近。我一直安慰自己韵吨,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布移宅。 她就那樣靜靜地躺著归粉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漏峰。 梳的紋絲不亂的頭發(fā)上糠悼,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音浅乔,去河邊找鬼倔喂。 笑死,一個胖子當著我的面吹牛靖苇,可吹牛的內容都是我干的席噩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贤壁,長吁一口氣:“原來是場噩夢啊……” “哼悼枢!你這毒婦竟也來了?” 一聲冷哼從身側響起脾拆,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤馒索,失蹤者是張志新(化名)和其女友劉穎莹妒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绰上,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡旨怠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了渔期。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片运吓。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡渴邦,死狀恐怖疯趟,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情谋梭,我是刑警寧澤信峻,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站瓮床,受9級特大地震影響盹舞,放射性物質發(fā)生泄漏。R本人自食惡果不足惜隘庄,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一踢步、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丑掺,春花似錦获印、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至唆缴,卻和暖如春鳍征,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背面徽。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工艳丛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趟紊。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓质礼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親织阳。 傳聞我的和親對象是個殘疾皇子眶蕉,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容