关于block的所有问题

关于block捕获外部的变量

(1)局部变量
(2)静态变量
(3)全局变量
(4)全局静态变量
(6)block捕获外边的变量在里面可以更改变量的值吗?可更改是为什么,不可更改是为什么.
(5)block捕获基本类型变量和捕获对象类型变量的区别

关于block的类型

(1)堆block
(2)栈block
(3)全局block
(4)block在什么情况下分别是什么类型

关于block在MRC和在ARC注意事项

(1)block之后copy 分别在MRC, ARC会发生什么
(2)block会不会持有捕获的对象变量

关于循环引用__weak

(1)__weak对象或者__strong对象,block是怎么对捕获的对象进行内存管理的

关于__block

(1)__block修饰后的变量本质是什么
(2)在block内部为什么不能修改外边的局部变量

我们先看看下面代码根据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){
            //参数
            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++结构体的构造函数,可以理解成OC的初始化方法

根据上面的解释我们可以看出lc_block其实就是指向__main_block_impl_0结构体的指针到这里我们其实已经可以看到lc_block是怎么捕获外边的变量了__main_block_impl_0构造函数的 第一个参数(void)__main_block_func_0 就是block的实现函数指针, 第二个参数&__main_block_desc_0_DATA暂时先不做解释,第三,第四个参数autoa, &statica就是我们捕获外边的变量 ,说道这里我们至少明白了一个问题
(1)block是怎么捕获外边的变量的.
下面我们回答一下这两个问题 block是在__main_block_impl_0初始化的时候把需要的捕获的变量令作为参数传进__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就是我们在初始化结构体穿进去的第一个参数就是__main_block_func_0函数的指针,这里大家可能有个疑问按照正常的写法不应该是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这个函数(再次强调一下这个函数是block的实现)这个函数的第一个参数就是block的指针.第二个第三个参数是我们自己传进去的.
看到这里我们可以基本可以回答两个问题.

(1)block捕获会捕获外边的什么变量

下面我们回答一下这两个问题block里面用到的变量block才会捕获但是block不会捕获 全局变量, static静态变量(可以🤔一下为啥block不会捕获全局变量,全局静态变量) 细心的朋友可能还发现了block捕获局部变量autoa是值传递,捕获局部静态变量statica是指针传递.这也就说明为啥block中可以修改静态变量不可以修改局部变量了(可以🤔一下为啥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类型 环境
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作为函数返回值时
将block赋值给__strong指针时
block作为Cocoa API中方法名含有usingBlock的方法参数时
block作为GCD API的方法参数时

不知道大家有没有思考过这个问题编译器为什么要把栈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

我们发现打印的autoa这个变量是个奇怪的值原因是lc_blcok是NSStackBlock虽然我们用一个全局指针lc_blcok指向这个block(也可以说是结构体 因为我们通过前面的代码分析出来block其实就是一个结构体的指针)但是这个结构体是在栈 上呢,在栈上的数据有个特点就是他的内存管理是系统控制的, lc_test函数以调用完毕内存就可呢释放了 所以block捕获进去的autoa也是垃圾内存了. 所以编译器会根据情况自动将栈上的block复制到堆上.

说道这里还能引申一个问题就是在block作为一个类的属性时到底是用strong 修饰还是用copy修饰呢
结论是
ARC的情况下 用strong和copy 都可以
MRC情况下建议用copy

原因相信聪明的你已经知道了

block捕获对象类型是否持有auto对象
下面我们根据 clang命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m再看看转成c++的代码 (我们考虑到有__weak的情况现在只讨论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这个结构体里面多了两个函数指针变量
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);

下面就是函数指针变量的实现 __main_block_copy_0函数会在block从栈被拷贝到堆上自动调用,
__main_block_dispose_0函数会在堆上的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函数和__main_block_dispose_0里面的_Block_object_dispose函数对auto对象变量做内存方面的管理 那我们是不是人为 只要是__strong修饰的对象block就会持有(是对象的引用计数+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++关键代码 我们发现
虽然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函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会根据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修饰的是对象变量这个结构体中会有两个函数指针__Block_byref_id_object_copy,和__Block_byref_id_object_dispose分别对对象变量做内存方面的管理,结构体里面有个重要的指针__forwarding是指想自己的指针.
我们从block的实现中__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的指针)再拿到真正的变量然后修改

现在大家可能有一个疑问为啥不通过包装成block的指针直接拿到真正的变量
(testObj1->__forwarding->testObj1) = __null;
(autoa->__forwarding->autoa)++;

image.png

image.png

我们根据上面的图可以看出当block在栈上包装__block修饰的变量的结构体中__forwarding是指向自己,当block通过copy拷贝到堆上包装__block修饰的变量的结构体中__forwarding指向的就是堆上的, 猜测难道是我们再__main_block_func_0这个函数中使用的__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存在的意义,哪位大佬了解拜托指教一二

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容