iOS Block 底层原理

  1. Block 结构体写法
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;
  int age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    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, int b) {
  int age = __cself->age; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_1);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_2);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_3);
        }

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 age = 10;

        // - (void(*)(int,int)) 是强制类型转换
        /* 本质
         block = &__main_block_impl_0(  __main_block_func_0,
                                        &__main_block_desc_0_DATA,
                                        age));

         */
        void (*block)(int, int) = ((void(*)(int,int))&__main_block_impl_0(
                                (void*)__main_block_func_0,
                                &__main_block_desc_0_DATA,
                                age));

        // - (void (*)(__block_impl *, int, int)) 是强制类型转换
        /* 本质
            block->FuncPtr(block, 10, 10);
         */
        ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)(
                                                                               (__block_impl *)block,
                                                                               10,
                                                                               10);
    }
    return 0;
}
  1. block 中的参数类型
// - 有参数的 block 的数据类型的源码(以上的 block 的内部结构是clang编译的结果 具有一定的参考意义 下边的是逆向工程中的block 的使用)

#import <Foundation/Foundation.h>

// -  block 在内存中的结构
typedef struct  {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy_helper)(void *dst, void *src);
    void (*dispose_helper)(void *src);
    const char *signature;
}QGBlockDescriptor;

// -  block 在内存中的结构
typedef struct  {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    QGBlockDescriptor *descriptor;
} QGBlockLiteral;

@interface QGBlockTool : NSObject

/** 输出逆向的 block 的说明 */
+ (NSMethodSignature *)printBlockDescriptor:(QGBlockLiteral *)blockStruct;

@end

#import "QGBlockTool.h"

@implementation QGBlockTool
// - 获取 block 的返回值和参数的签名和 block 的实现函数指针
/** 输出逆向的 block 的说明 */
+ (NSMethodSignature *)printBlockDescriptor:(QGBlockLiteral *)blockStruct{
    
    /** 判断 block 是否有签名 */
    BOOL hasSign = ((blockStruct -> flags  & (1 << 30)) != 0);
    if (!hasSign) return nil;
    
    // - block 的函数地址
    NSLog(@"调用地址 : %p", blockStruct->invoke);
    
    // - block 的函数指针
    QGBlockDescriptor *descriptor = blockStruct->descriptor;
    NSLog(@"descriptor : %@", [NSString stringWithUTF8String:descriptor -> signature]);
    NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:descriptor -> signature];
    return methodSignature;
}
@end

  1. block 对变量的捕获 (考虑作用域的问题,需要跨函数访问,为了保证block内部能够正常访问外部的变量,block有个变量捕获机制){
    1. auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;static变量一直保存在内存中,指针访问即可
    2. block不需要对全局变量捕获,都是直接采用取值的
      }

[示例如下]:
block 对局部变量的捕获 (a 为 局部变量, b 为静态局部变量)
在这里插入图片描述
block 对成员变量和 self 的捕获(以为 self 和 成员变量属于 方法的参数和需要方法的参数查找的变量, 所以也是属于捕获局部变量, 需要捕获)
在这里插入图片描述block 对全局变量的捕获 (a 为 全局变量, b 为静态全局变量)
在这里插入图片描述
[结论]:
在这里插入图片描述

  1. block 对变量的引用操作(为什么有__weak typeof(self) weakSelf = self; 和 __strong typeof(self) strongSelf = weakSelf;)
    因为 block 是一个代码块, 是由程序员自己调用的, 而程序员调用 block 时候, 可能 block 内部访问的局部对象已经释放, 为了防止这样已经被释放的变量再次被访问而出错, 所以 block 会对内部的访问的变量有一次强引用操作, 如下:
    代码:
// - OC 代码
__block __weak int age = 10;

// - 修改对象类型 也必须要加 __block
__block NSObject *obj = [[NSObject alloc]init];
MJBlock block1 = ^{
    __strong int myage = age;
    age = 20;
    obj = nil;
    NSLog(@"age is %d", age);
};

MJBlock block2 = ^{
    age = 30;
    NSLog(@"age is %d", age);
};

block1();
block2();


// - 访问成员变量的 block 的内部实现代码 
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;
  NSObject *p;
  __Block_byref_age_0 *age; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__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_age_0 *age = __cself->age; // bound by ref
  NSObject *p = __cself->p; // bound by copy

            (age->__forwarding->age) = 20;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_e2457b_mi_0, p);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->p, 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};

block 对局部变量的捕捉和引用问题

  • 如果block在栈空间,不管外部变量是强引用还是弱引用,block都会弱引用访问对象
  • 如果block在堆空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用

[结论 : ]

  • 栈block
    a. 如果block是在栈上,将不会对auto变量产生强引用
    b. 栈上的block随时会被销毁,也没必要去强引用其他对象

  • 堆block
    a. 如果block被拷贝到堆上:

    1. 会调用block内部的copy函数
    2. copy函数内部会调用_Block_object_assign函数
    3. _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

    b. 如果block从堆上移除

    1. 会调用block内部的dispose函数
    2. dispose函数内部会调用_Block_object_dispose函数
    3. _Block_object_dispose函数会自动释放引用的auto变量(release)
//  -  测试 (一)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    Person *person = [[Person alloc] init];
    person.age = 10;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"age:%d",person.age);
    });
      NSLog(@"touchesBegan");
}
[打印] : touchesBegan   age:10    Person-dealloc

//  -  测试 (二)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    Person *person = [[Person alloc] init];
    person.age = 10;
    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"age:%p",weakPerson);
    });
    NSLog(@"touchesBegan");
}
[打印] : touchesBegan   Person-dealloc    age:0x0

//  -  测试 (三)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    Person *person = [[Person alloc] init];
    person.age = 10;
    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{  
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-----age:%p",person);
        });
        NSLog(@"1-----age:%p",weakPerson);
    });
    NSLog(@"touchesBegan");
}
[打印] : touchesBegan   1-----age:0x604000015eb0    2-----age:0x604000015eb0    Person-dealloc

//  -  测试 (四)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    Person *person = [[Person alloc] init];
    person.age = 10;
    __weak Person *weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{          
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-----age:%p",weakPerson);
        });
        NSLog(@"1-----age:%p",person);
    });
    NSLog(@"touchesBegan");
}
[打印] : touchesBegan   1-----age:0x6000000178d0   Person-dealloc   2-----age:0x0
  1. __block 修饰的变量
// - __block 的使用
int b = 20;
__block int age = 10;

MJBlock block = ^{
    age = 20;
    NSLog(@"age is %d", age);
};
struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)block;
((void (*)(void *))blockImpl->impl.FuncPtr)(blockImpl);


// - __block 修饰的变量的内部的代码  如下 age 被包装成一个对象
struct __Block_byref_age_0 {
    void *__isa;
    struct __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age;
};

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

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;
    struct __Block_byref_age_0 *age;
};

[__block 的使用] :

  1. __block可以用于解决block内部无法修改auto变量值的问题(auto修饰变量,block无法修改,因为block使用的时候是内部创建了变量来保存外部的变量的值,block只有修改内部自己变量的权限,无法修改外部变量的权限。)

  2. __block不能修饰全局变量、静态变量(static)(因为全局变量, block 内部可以直接访问到,不用捕获; static 变量, 是指针传递, 也是可以直接修改的)

  3. __block 修饰的变量, 内部的实质是将 block 内部要访问的那个变量,包装成为一个对象, 这样就可以随意赋值了;

  4. 当block被copy到堆时,copy函数内部会调用_Block_object_assign函数, _Block_object_assign函数会对__block变量形成强引用(retain)对于__block 修饰的变量 assign函数对其强引用;对于外部对象 assign函数根据外部如何引用而引用;

  5. block 的使用(少见的 block 的定义属性的方法)

// - 定义 block
typedef  QGLabel *(^Block3)(UIColor *color);
@interface QGLabel : UILabel
@property (nonatomic, copy) Block3 block2;
@property (nonatomic, copy) Block3 block3;
@property (nonatomic, copy) NSString *(^block)(NSNumber *);

@end

@implementation QGLabel
#pragma mark - 设置初始化数据
/** 设置数据 */
-(void)setupData{
 self.h = ^NSString *(NSNumber *num) {
        NSLog(@"%@", num);
    };

    __weak typeof(self) weakSelf = self;
    self.block2 = ^QGLabel *(UIColor *color) {
        weakSelf.textColor = color;
        return weakSelf;
    };
}
    
-(Block3)block3{
    __weak typeof(self) weakSelf = self;
    return ^QGLabel *(UIColor *color) {
        weakSelf.backgroundColor = color;
        return weakSelf;
    };
}

/** 测试调用 */
-(void)test{
    // - setter
    self.block = ^NSString *(NSNumber *num) {
        return [NSString stringWithFormat:@"%@", num];
    };
    
    // - getter
    self.block(@(1));
}

/** block 的 setter */
-(void)setBlock:(NSString *(^)(NSNumber *num))block{
    
    NSString *str =  [NSString stringWithFormat:@"block 的 setter  %@", block(@1)];
    NSLog(@"str : %@", str);
}

/** block 的 getter */
-(NSString *(^)(NSNumber *))block{
    return ^NSString *(NSNumber *num){
        NSString *str = [NSString stringWithFormat:@"block 的 getter  %@", num];
        NSLog(@"str : %@", str);
        return str;
    };
}

@end

// - block中传递 self ,使self及时释放
- (void)asyncRun:(void(^)(MLVBLiveRoom *self))block {
    __weak __typeof(self) wself = self;
    dispatch_async(_queue, ^{
        __strong __typeof(wself) self = wself;
        if (self) {
            block(self);
        }
    });
}

block 中使用 __weak

// - 视图控制器
- (void)viewDidLoad {
    [super viewDidLoad];
    self.redView = [[RedView alloc]initWithFrame:CGRectMake(90, 90, 90, 90)];
    self.redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.redView];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.redView removeFromSuperview];
    self.redView = nil;
}

/*  以下代码中 (在 property的声明中可以不用__weak 修饰)
// - 声明中可以不写 __weak; 即:  
	@property (nonatomic, copy) void(^block)( id xx); 但是后边的赋值,必须写__weak,否则不会及时释放
	@property (nonatomic, copy) void(^block)(__weak id xx);
	self.block = ^(__weak id xx) {
            [NSTimer scheduledTimerWithTimeInterval:10 repeats:NO block:^(NSTimer * 		_Nonnull timer) {
                NSLog(@"xx : %@", xx);
            }];
        };
*/
1. 如果 block 中的 xx 变量, 如果没有使用__weak 修饰, touch时候, 则不会立即 dealloc ,需要计时器 10s 后回调block打印 xx : <RedView: 0x7faabc411830; frame = (90 90; 90 90); layer = <CALayer: 0x6000028d8920>> 之后才会 dealloc
2. 如果 block 中的 xx 变量, 如果有__weak 修饰, touch时候, 会立即 dealloc ,然后计时器 10s 后打印 xx : (null)

// -  redView
@interface RedView()
@property (nonatomic, copy) void(^block)(__weak id xx);
@end

@implementation RedView
- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]) {
        self.block = ^(__weak id xx) {
            [NSTimer scheduledTimerWithTimeInterval:10 repeats:NO block:^(NSTimer * _Nonnull timer) {
                NSLog(@"xx : %@", xx);
            }];
        };
        self.block(self);
    }
    return self;
}

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值