美文网首页iOS源码解析
iOS 动态调用Block

iOS 动态调用Block

作者: 哦呵呵y | 来源:发表于2018-03-13 16:10 被阅读9次

关于block 内部实现 ,大多数博客都是使用 clang -rewrite-objc main.m 来查看c++源码:

struct __Person__init_block_impl_0 {
  struct __block_impl impl;
  struct __Person__init_block_desc_0* Desc;
  Person *self;
  __Person__init_block_impl_0(void *fp, struct __Person__init_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static struct __Person__init_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __Person__init_block_impl_0*, struct __Person__init_block_impl_0*);
  void (*dispose)(struct __Person__init_block_impl_0*);
} __Person__init_block_desc_0_DATA = { 0, sizeof(struct __Person__init_block_impl_0), __Person__init_block_copy_0, __Person__init_block_dispose_0};

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

解析出来的c++ 源码,其中block 由一个结构体实现。很多文章都有解析block 的,但是基本上都是说 __block_impl-> Flags 未预留字段,由于没有实际用到,基本也不会深究,直到看到AOP库Aspects和RunTime库CTObjectiveCRuntimeAdditions还有JavascriptCore才发现,原来block可以用NSInvocation来动态调用。

  • 用过NSInvocation的应该知道, 它可以用来实现对指定对象的任意方法的调用
  • 使用NSInvocation调用block 流程 并没有selector, 调用逻辑是:
    获取block的signature->设置target为block->设置参数->invoke 并不需要想普通方法一样需要selector
  • NSInvocation 通过NSMethodSignature 来创建

那么通过以上几点点,使用NSInvocation我们就需要获取block的Signature,在JavascriptCore中使用了 _Block_has_signature 和 _Block_signature来获取Signature,但是这两个函数又属于私有API,而AspectsCTObjectiveCRuntimeAdditions中都是使用block 结构体中的字段来实现的,但是又发现他们定义的结构和clang -rewrite-objc main.m 解析出来的不大一样。直到看到了Clang 7 文档 才发现他们使用的block和这上面的一模一样。

struct Block_literal_1 {
    void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor_1 {
    unsigned long int reserved;         // NULL
        unsigned long int size;         // sizeof(struct Block_literal_1)
        // optional helper functions
        void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
        void (*dispose_helper)(void *src);             // IFF (1<<25)
        // required ABI.2010.3.16
        const char *signature;                         // IFF (1<<30)
    } *descriptor;
    // imported variables
};

他们用到了其中的flags和signature,其中flags是一个枚举值

enum {
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
    BLOCK_HAS_CTOR =          (1 << 26), // helpers have C++ code
    BLOCK_IS_GLOBAL =         (1 << 28),
    BLOCK_HAS_STRET =         (1 << 29), // IFF BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE =     (1 << 30),
};

结合上面block发现 flags 标识 Block_descriptor_1结构体中的结构

        struct CTBlockLiteral *blockRef = (__bridge struct CTBlockLiteral *)block;
        _flags = blockRef->flags;
        _size = blockRef->descriptor->size;
        
        if (_flags & CTBlockDescriptionFlagsHasSignature) {
            void *signatureLocation = blockRef->descriptor;
            signatureLocation += sizeof(unsigned long int);
            signatureLocation += sizeof(unsigned long int);
            
            if (_flags & CTBlockDescriptionFlagsHasCopyDispose) {
                signatureLocation += sizeof(void(*)(void *dst, void *src));
                signatureLocation += sizeof(void (*)(void *src));
            }
            
            const char *signature = (*(const char **)signatureLocation);
            _blockSignature = [NSMethodSignature signatureWithObjCTypes:signature];
        }

上面为CTObjectiveCRuntimeAdditions的代码与Aspects实现基本相同。
都是通过将block转换为自定义与block相同结构的结构体。由于结构体中Block_descriptor_1结构不固定,所以只有通过指针从首地址进行偏移来获取到最后的signature;
然后使用signature创建NSInvocation,然后设置Target为block,设置参数然后调用invoke。

参考文档

clang7文档
NSInvocation动态调用任意block
CTObjectiveCRuntimeAdditions
Aspects

相关文章

  • iOS 动态调用Block

    关于block 内部实现 ,大多数博客都是使用 clang -rewrite-objc main.m 来查看c...

  • iOS动态调用类方法

    iOS动态调用类方法(不带参数) iOS动态调用类方法(带参数) iOS动态调用类方法(有返回值) 优点 弱化连接...

  • GCD延时任务取消

    1.dispatch_block_cancel iOS8之后可以调用dispatch_block_cancel来取...

  • 如何取消GCD任务

    1.dispatch_block_cancel iOS8之后可以调用dispatch_block_cancel来取...

  • 底层原理之Block

    iOS 面试集合之block#### block1. 本质:block是个结构体对象,封装了函数调用```obje...

  • [iOS]libffi动态调用C函数

    前言:在iOS开发中可以使用Runtime动态调用OC方法,但是无法动态调用C函数,那么该如何动态调用C函数呢?值...

  • 解析 iOS 中的 Block

    1. OC中block的用法 iOS 中block被用于嵌套方法间传递,并在方法调用返回时,通过block回调返回...

  • Aspects源码解析之Block的动态调用

    1、前言 我们在分析Block的动态调用之前,先简单了解一下消息的转发机制。作为一个iOS开发者,消息的转发机制应...

  • iOS-2 Block

    block块 系列文章: iOS Block浅浅析 - 简书 iOS Block实现原理 iOS Block __...

  • block的基本使用

    什么是block block是ios中一种特殊的数据类型 block的作用用来保存一段代码,可以在恰当的时候调用功...

网友评论

    本文标题:iOS 动态调用Block

    本文链接:https://www.haomeiwen.com/subject/nxuzfftx.html