iOS-底层原理(8)-block-本质,类型,copy属性详解

1.Block的本质

  • block本质上也是一个OC对象,它内部也有个isa指针
  • block是封装了函数调用以及函数调用环境的OC对象
  • block的底层结构如下图所示
iOS-底层原理(8)-block-本质,类型,copy属性详解_第1张图片
image.png
iOS-底层原理(8)-block-本质,类型,copy属性详解_第2张图片
image.png

代码佐证

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
};

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

// block底层结构体
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age;
};

// 1.block结构体
void test1() {
    int age = 10;
    
    // 定义block
    void(^block)(int, int) = ^(int a, int b){
        NSLog(@"this is a block! -- %d", age);
        NSLog(@"a = %d, b = %d",a,b);
    };
    
    struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
    block(100,200);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 1. block结构体
        test1();
    }
    return 0;
}
iOS-底层原理(8)-block-本质,类型,copy属性详解_第3张图片
image.png
2.block的变量捕获(capture)
  • 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
iOS-底层原理(8)-block-本质,类型,copy属性详解_第4张图片
image.png
  • auto:自动变量,离开作用域就销毁

代码例子如下:

// 捕获变量类型
void (^block)(void);

void blockTest() {
    int age = 10;
    static int height = 10;
    
    block = ^ {
        NSLog(@"age is %d, height is %d", age, height);
    };
    
    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        blockTest();
        block();
    }
    return 0;
}

运行结果

image.png
Block的本质 - NSObject对象
iOS-底层原理(8)-block-本质,类型,copy属性详解_第5张图片
image.png
3.block的类型
  • block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
  • __NSGlobalBlock__ (_NSConcreteGlobalBlock )
  • __NSStackBlock__ ( _NSConcreteStackBlock )
  • __NSMallocBlock__ ( _NSConcreteMallocBlock )
iOS-底层原理(8)-block-本质,类型,copy属性详解_第6张图片
image.png
iOS-底层原理(8)-block-本质,类型,copy属性详解_第7张图片
image.png
  • 每一种类型的block调用copy后的结果如下所示
iOS-底层原理(8)-block-本质,类型,copy属性详解_第8张图片
image.png

代码例子如下

  • 先将环境切换为MRC,setting -> OC Automitic RF -> NO
// 4.block的类型
int weight = 100;
void (^block)(void);

void blockClassType() {
    // 堆:动态分配内存,需要程序员申请申请,也需要程序员自己管理内存
    static int age = 10;
    // 局部static变量
    int height = 10;
    
    // Global:没有访问auto变量
    void(^block1)(void) = ^ {
        NSLog(@"block1");
    };
    // Globa2:访问static变量
    void(^block2)(void) = ^ {
        NSLog(@"block2 - age = %d",age);
    };
    // Globa3:访问全局变量
    void(^block3)(void) = ^ {
        NSLog(@"block3 - weight = %d",weight);
    };
    
    // Stack:访问了auto变量
    void (^block4)(void) = ^{
        NSLog(@"block4 - height = %d", height);
    };
    
    // NSMallocBlock - 对StackBlock做copy操作
    block = [^{
        NSLog(@"block---------%d", height);
    } copy];
    [block release];
    
    NSLog(@"%@ %@ %@ %@ %@",
          [block1 class],
          [block2 class],
          [block3 class],
          [block4 class],
          [block class]
    );
}

运行结果如下

image.png
4. 数据存储位置

代码例子如下

// 5.数据存储位置
int age = 100;
void dataLocationTest() {
    int a = 10;
    
    NSLog(@"数据段:age %p", &age);
    NSLog(@"栈:a %p", &a);
    NSLog(@"堆:obj %p", [[NSObject alloc] init]);
    NSLog(@"数据段:class %p", [Person class]);
}

打印结果

iOS-底层原理(8)-block-本质,类型,copy属性详解_第9张图片
image.png
5.block的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

  • block作为函数返回值时
  • 将block赋值给__strong指针时
  • block作为Cocoa API中方法名含有usingBlock的方法参数时
  • block作为GCD API的方法参数时

代码例子如下

  • 1.block作为函数返回值时
// 定义一个block
typedef void(^CSBlock)(void);

// 定义一个返回blcok的函数
CSBlock myBlock() {
    int age = 10;
    return ^{
        NSLog(@"age = %d",age);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 1.block作为函数返回值
        CSBlock block = myBlock();
        block();
        NSLog(@"%@",[block class]);
    }
    return 0;
}

运行结果

image.png
  • 2.将block赋值给__strong指针时
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 2.block有被强指针引用
        // ARC - 环境下 block - __NSMallocBlock__
        // MRC - 环境下 block - __NSStackBlock__
        int age = 10;
        CSBlock block = ^{
            NSLog(@"---------%d", age);
        };
        NSLog(@"%@", [block class]);
    }
    return 0;
}

打印结果

ARC.png
MRC.png
  • 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
// 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
NSArray *array = [NSArray array];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            
}];
  • 4.block作为GCD API的方法参数时
// 4.block作为GCD API的方法参数时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{            
});
block建议写法
  • MRC下block属性的建议写法
    @property (copy, nonatomic) void (^block)(void);

  • ARC下block属性的建议写法
    @property (strong, nonatomic) void (^block)(void);
    @property (copy, nonatomic) void (^block)(void);


本文参考借鉴MJ的教程视频,非常感谢.


项目演示代码如下
iOS_block_本质
iOS-block-copy


更多block相关文章

iOS-copy底层原理之auto变量

iOS-block底层原理详解之__block属性

iOS-block底层原理之循环引用详解

你可能感兴趣的:(iOS-底层原理(8)-block-本质,类型,copy属性详解)