OC Block底层

block种类

1.__NSGlobalBlock__

block无参无返回值

void(^block)(void) = ^{
        NSLog(@"aaaaaaa");
    };
    NSLog(@"%@", block);
aaa[6077:155418] <__NSGlobalBlock__: 0x10d32e0e0>

2.__NSMallocBlock__

block访问外界变量

int a = 100;
    void(^block)(void) = ^{
        NSLog(@"aaaaa - %d", a);
    };
    NSLog(@"%@", block);
aaa[6346:163587] <__NSMallocBlock__: 0x6000013a9a10>

3.__NSStackBlock__ 

block访问外界变量,同时用weak弱引用

int a = 100;
    void(^__weak block)(void) = ^{
        NSLog(@"aaaaa - %d", a);
    };
    NSLog(@"%@", block);
aaa[6485:167690] <__NSStackBlock__: 0x7ff7b7e3bae8>

总结

  • block没访问外界变量则直接存储在全局区
  • block访问外界变量引用,存储在
  • block访问外界变量引用,存储在

循环引用

//代码一
    NSString *name = @"AAA";
    self.block = ^(void){
        NSLog(@"%@",self.name);
    };
    self.block();

    //代码二
    UIView animateWithDuration:1 animations:^{
        NSLog(@"%@",self.name);
    };

代码1中,因为self持有block,block访问了self.name导致block也持有了self,从而self和block互相持有,所以代码1发生了循环引用。 代码二不构成相互持有。下面介绍几种方法解决循环引用:

1.weak-stong-dance

__weak typeof(self) weakSelf = self;
self.slBlock = ^(void){
    __strong typeof(weakSelf) strongSelf = weakSelf;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",strongSelf.name);
    });
};
self.slBlock();

__weak修饰self,打破self对block的强引用 ,如果block里边嵌套block,需同时使用__weak和__strong

2.__block修饰变量

__block ViewController *vc = self;
self.slBlock = ^(void){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",vc.name);
        vc = nil;//手动释放
    });
};
self.sllBlock();

block必须要调用,否则依旧存在循环引用

3.self作为block参数


self.slBlock = ^(ViewController *vc){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",vc.name);
    });
};
self.slBlock(self);

4.NSProxy 虚拟类

NSProxy可实现伪多继承,参考YYkit的YYWeak,NSProxy就解决了NSTimer和CADisplayLink创建时对self强引用问题。NSProx就像一个代理人,可通过继承它,并重写消息慢速转发的那两个方法(forwardInvocation、methodSignatureForSelector)实现消息转发

自定义一个NSProxy

@interface SLProxy : NSProxy
- (id)transformObjc:(NSObject *)objc;
+ (instancetype)proxyWithObjc:(id)objc;
@end

@interface SLProxy()
@property(nonatomic, weak, readonly) NSObject *objc;
@end

@implementation SLProxy
- (id)transformObjc:(NSObject *)objc{
   _objc = objc;
    return self;
}
+ (instancetype)proxyWithObjc:(id)objc{
    return  [[self alloc] transformObjc:objc];
}
- (BOOL)respondsToSelector:(SEL)aSelector{
    return [self.objc respondsToSelector:aSelector];
}
//1、查询该方法的方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
    NSMethodSignature *signature;
    if (self.objc) {
        signature = [self.objc methodSignatureForSelector:sel];
    }else{
        signature = [super methodSignatureForSelector:sel];
    }
    return signature;
}
//2.有了方法签名之后就会调用方法实现
- (void)forwardInvocation:(NSInvocation *)invocation{
    SEL sel = [invocation selector];
    if ([self.objc respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.objc];
    }
}

自定义Cat、Bird类

#import "ViewController.h"
#import "SLProxy.h"

//********Cat类********
@interface Cat : NSObject
@end
@implementation Cat
- (void)eat{
   NSLog(@"我爱吃鱼");
}
@end

//********Bird类********
@interface Bird : NSObject
@end

@implementation Bird
- (void)fly{
    NSLog(@"我会飞");
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self cjl_proxyTest];
}

- (void)cjl_proxyTest{
    Bird *bird = [[Bird alloc] init];
    Cat *cat = [[Cat alloc] init];
    SLProxy *proxy = [SLProxy alloc];
    
    [proxy transformObjc:cat];
    [proxy performSelector:@selector(eat)];
    
    [proxy transformObjc:bird];
    [proxy performSelector:@selector(fly)];
}


@end

SLProxy既爱吃鱼,也可以飞,具备了猫和鸟的能力

通过NSProxy解决定时器self得强引用

self.timer = [NSTimer timerWithTimeInterval:1 target:[SLProxy proxyWithObjc:self] selector:@selector(print) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

Block底层

定义一个block

- (void)viewDidLoad {
    [super viewDidLoad];

    void(^block)(void) = ^{
        printf("aaaaa");
    };
}

编译成cpp后结果如下

struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {

        printf("aaaaa");
    }

static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
}
// @end

相当于block等于__ViewController__viewDidLoad_block_impl_0,是一个函数。

查看__ViewController__viewDidLoad_block_impl_0定义,是一个结构体,同时可以说明block是一个__ViewController__viewDidLoad_block_impl_0类型的对象,这也是为什么block能够%@打印的原因。block本质对象、函数、结构体,由于block函数没有名称,也被称为匿名函数。

1.block是需要调用的

函数声明:block内部实现声明成了一个函数__ViewController__viewDidLoad_block_func_0​​​​​​​

执行具体函数实现:通过调用block的FuncPtr指针,调用block执行

2.block捕获外界变量

- (void)viewDidLoad {
    [super viewDidLoad];

    int t = 10;
    void(^block)(void) = ^{
        printf("aaaaa - %d",t);
    };
    
    block();
}

编译成cpp

struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  int t;
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int _t, int flags=0) : t(_t) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
  int t = __cself->t; // bound by copy

        printf("aaaaa - %d",t);
    }

static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    int t = 10;
    void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, t));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

可知block捕获外界变量时,会在内部会自动生成同一个属性来保存

如果外界变量t用__block修饰,然后在block中对t进行修改,编译如下

struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  __Block_byref_t_0 *t; // by ref
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_t_0 *_t, int flags=0) : t(_t->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
  __Block_byref_t_0 *t = __cself->t; // bound by ref

        (t->__forwarding->t)++;
        printf("aaaaa - %d",(t->__forwarding->t));
    }
。。。
// @end

 ​​​​​​​外界变量会生成__Block_byref_a_0结构体,用来保存原始变量指针和值,在block内部即可对外部变量进行操作(即指针拷贝)。

Block最底层真正类型

查看​​​​​​​libclosure源码,可知block真正的类型,是一个结构体

// CJL注释:Block 结构体
struct Block_layout {
    //指向表明block类型的类
    void *isa;//8字节
    //用来作标识符的,类似于isa中的位域,按bit位表示一些block的附加信息
    volatile int32_t flags; // contains ref count 4字节
    //保留信息,可以理解预留位置,用于存储block内部变量信息
    int32_t reserved;//4字节
    //函数指针,指向具体的block实现的调用地址
    BlockInvokeFunction invoke;
    //block的附加信息
    struct Block_descriptor_1 *descriptor;
    // imported variables
};

猜你喜欢

转载自blog.csdn.net/weixin_38016552/article/details/129197859