iOS Block底层实现原理详解

block原始代码为:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        int a = 10;
        
        void (^block)() = ^{
            printf("%d\n", a);
        };
        
        //再次给a 赋值
        
        a = 20;
        
        block();
    }
    return 0;
}
将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    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 = __cself->a; // bound by copy

            printf("%d\n", 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; 

        int a = 10;

        void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));



        a = 20;

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
block原始代码为:
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int a = 10;
        
        void (^block)() = ^{
            printf("%d\n", a);
        };
        
        //再次给a 赋值
        
        a = 20;
        
        block();
    }
    return 0;
}
将上面block原始代码转换为C++代码: 通过 命令 :clang -rewrite-objc main.m

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__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_a_0 *a = __cself->a; // bound by ref

            printf("%d\n", (a->__forwarding->a));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 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_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};

        void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));//这里的570425344 最后赋值给 flags 但是 flags 直接又被赋为 0,这个570425344在此处没用。




        (a.__forwarding->a) = 20;

        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };


所谓结构体类型的自动变量,即栈上生成的该结构体的实例。

设置对象的存储域
_NSConcreteStackBlock
_NSConcreteGlobalBlock 程序的代码段(.text区)
_NSConcreteMallocBlock


存储域

通过上面的描述我们知道Block和__block变量实质就是一个相应结构体的实例。我们在上述转换过的代码中可以发现 __main_block_impl_0 结构体构造函数中, isa指向的是 _NSConcreteStackBlock。Block还有另外两个与之相似的类:

  • _NSConcreteStackBlock 保存在栈中的block,出栈时会被销毁

  • _NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量

  • _NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁

上述示例代码中,Block是被设为_NSConcreteStackBlock,在栈上生成。当我们把Block作为全局变量使用时,对应生成的Block将被设为_NSConcreteGlobalBlock,如:

1
2
3
4
5
6
7
void (^block)(void) = ^{NSLog(@ "This is a Global Block" );};
int main(int argc, const char * argv[]) {
     @autoreleasepool {
         block();
     }
     return  0;
}

该代码转换后的代码中,Block结构体的成员变量isa的初始化如下:

1
  impl.isa = &_NSConcreteGlobalBlock;

那么_NSConcreteMallocBlock在什么时候被使用呢?

分配在全局变量上的Block,在变量作用域外也可以通过指针安全的访问。但分配在栈上的Block,如果它所属的变量作用域结束,该Block就被废弃。同样地,__block变量也分配在栈上,当超过该变量的作用域时,该__block变量也会被废弃。

这个时候_NSConcreteMallocBlock就登场了,Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。将分配到栈上的Block复制到堆上,这样但栈上的Block超过它原本作用域时,堆上的Block还可以继续存在。

复制到堆上的Block,它的结构体成员变量isa将变为:

1
impl.isa = &_NSConcreteMallocBlock;

而_block变量中结构体成员__forwarding就在此时保证了从栈上复制到堆上能够正确访问__block变量。在这种情况下,只要栈上的_block变量的成员变量__forwarding指向堆上的实例,我们就能够正确访问。

我们一般可以使用copy方法手动将 Block 或者 __block变量从栈复制到堆上。比如我们把Block做为类的属性访问时,我们一般把该属性设为copy。有些情况下我们可以不用手动复制,比如Cocoa框架中使用含有usingBlock方法名的方法时,或者GCD的API中传递Block时。

当一个Block被复制到堆上时,与之相关的__block变量也会被复制到堆上,此时堆上的Block持有相应堆上的__block变量。当堆上的__block变量没有持有者时,它才会被废弃。(这里的思考方式和objc引用计数内存管理完全相同。)

//
//  main.m
//  test_block_05
//
//  Created by admin on 4/28/16.
//  Copyright © 2016 jeffasd. All rights reserved.
//

#import 

void (^globalBlock)() =  ^(){
    NSLog(@"fsdfsdf");
    return;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        int foo = 3;
        NSLog(@"the foo address is %p", &foo);
        
        int foo1 = 4;
        NSLog(@"the foo1 address is %p", &foo1);
        
        int foo2 = 5;
        NSLog(@"the foo2 address is %p", &foo2);
        
        __block int a = 10;
        
//        int a = 10;
        
        NSLog(@"11 %p", &a); //0x7fff5fbff7f8 地址很大在栈中
        
        void (^block)() = ^{
            NSLog(@"block a address %p", &a); //0x7fff5fbff7f8 地址很小在堆中
            printf("%d\n", a);
        };
        
        //再次给a 赋值
        
        NSLog(@"11 block is %@", block);
        
        NSLog(@"%p", &a);
        
        a = 20;
        
        block();
        
        NSLog(@"block is %@", block);
        
        NSLog(@"the global block is %@", globalBlock);
        globalBlock();
        NSLog(@"the global block is %@", globalBlock);
        
        __weak void (^weakBlock)() =  ^(){
            NSLog(@"fsdfsdf");
            return;
        };
        
        NSLog(@"the weak block is %@", weakBlock);
        
    }
    return 0;
}


//int main(int argc, const char * argv[]) {
//    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//        
//        int a = 10;
//        
//        void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
//        
//        
//        
//        a = 20;
//        
//        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
//    }
//    return 0;
//}
//static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

//切换成C++语音
//int main(int argc, const char * argv[]) {
//    @autoreleasepool {
//        
//        __block int a = 10;
//        
//        void (^block)() = ^{
//            printf("%d\n", a);
//        };
//        
//        //再次给a 赋值
//        
//        a = 20;
//        
//        block();
//    }
//    return 0;
//}
////切换成C++语音
//int main(int argc, const char * argv[]) {
//    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//        
//        __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
//        
//        void (*block)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
//        
//        
//        
//        (a.__forwarding->a) = 20;
//        
//        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
//    }
//    return 0;
//}
//static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

block属性

这里还有一点关于block类型的ARC属性。上文也说明了,ARC会自动帮strong类型捕获外部变量的block进行copy,所以在定义block类型的属性时也可以使用strong,不一定使用copy。也就是以下代码:

/** 假如有栈block赋给以下两个属性 **/

// 这里因为ARC,当栈block中会捕获外部变量时,这个block会被copy进堆中
// 如果没有捕获外部变量,这个block会变为全局类型
// 不管怎么样,它都脱离了栈生命周期的约束

@property (strong, nonatomic) Block *strongBlock;

// 这里都会被copy进堆中
@property (copy, nonatomic) Block *copyBlock;



你可能感兴趣的:(iOS_OC)