美文网首页
又是Block小结

又是Block小结

作者: xiaoliang1 | 来源:发表于2019-12-23 15:18 被阅读0次

    之前说了block的小探究,大家都知道block是个类。但是他又不像类那样调用方法。参数啥、回调函数都在结构体里面。
    NSConcreteGlobalBlock。这种很简单。点进去都可以看到相关结构。
    NSConcreteStackBlock,基本上回调函数存在X8寄存器.
    最近我发现在IOS13打印Block有了更清楚的信息:

    <__NSMallocBlock__: 0x2809553b0>
     signature: "v16@?0@"NSString"8"
     invoke   : 0x10227dcf4 (/private/var/containers/Bundle/Application/893409BD-1E16-4DBC-8D29-7DDC1355E2A5/dylibLoad.app/dylibLoad`__57-[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke)
     copy     : 0x10227ddc0 (/private/var/containers/Bundle/Application/893409BD-1E16-4DBC-8D29-7DDC1355E2A5/dylibLoad.app/dylibLoad`__copy_helper_block_e8_32r)
     dispose  : 0x10227de04 (/private/var/containers/Bundle/Application/893409BD-1E16-4DBC-8D29-7DDC1355E2A5/dylibLoad.app/dylibLoad`__destroy_helper_block_e8_32r)
    

    这个效果只有在IOS13系统上打印blcok才有的。
    signature就是这个invoke(回调函数)返回值和参数列表的TypeEncode.
    invoke就是这个回调函数所在文件的名字和地址,如果回调方法符号没被才会显示正确的方法名,不然就是内存地址。
    copy 和dispose 不知,不做研究。
    后来经过查看,在其父类NSBlock类上重写了-(void)debugDescription;方法。
    汇编代码如下:


    E5EED2D8-01A3-478E-A00F-6D52060000A9.png

    这里面包含了一个Block_signature函数。获取方法返回值和参数列表,汇编代码:


    WX20191223-142124@2x.png

    翻译成代码如下:

    NSString * getBlockDebugDescription(struct CDBlock *blcok) {
    
    
        NSMutableString *debugDescription = [NSMutableString stringWithFormat:@"\n<%@: %p>\n",blcok->isa,blcok];
    
        NSString *block_signatur_string = getBlock_signature(blcok);
        [debugDescription appendFormat:@" signature: %@\n",block_signatur_string];
    
        Dl_info invokeInfo = {0};
        if(dladdr(blcok->funk, &invokeInfo) != 0) {
            [debugDescription appendFormat:@" invoke   : %p\n (%s`%s offset:%p)\n",invokeInfo.dli_saddr,invokeInfo.dli_fname,invokeInfo.dli_sname,invokeInfo.dli_saddr - invokeInfo.dli_fbase];
        }
    
        Dl_info copyInfo = {0};
        if(dladdr(blcok->descriptor->copy, &copyInfo) != 0) {
            [debugDescription appendFormat:@" copy     : %p\n (%s`%s offset:%p)\n",copyInfo.dli_saddr,copyInfo.dli_fname,copyInfo.dli_sname,copyInfo.dli_saddr - copyInfo.dli_fbase];
        }
        Dl_info disposeInfo = {0};
        if(dladdr(blcok->descriptor->dispose, &disposeInfo) != 0) {
            [debugDescription appendFormat:@" dispose  : %p\n (%s`%s offset:%p)",disposeInfo.dli_saddr,disposeInfo.dli_fname,disposeInfo.dli_sname,disposeInfo.dli_saddr - disposeInfo.dli_fbase];
        }
        return debugDescription;
    }
    
    NSString* getBlock_signature(struct CDBlock *blcok) {
    
        NSString *signature = nil;
        int32_t pp11 =  0x2000000&blcok->flags; //此处已经判断是哪种descriptor了,
        //CDBlockDescriptor signature 偏移0x20。
        //CDBlockDescriptor1 signature 偏移 0x10
        //不知道在外面在获取copy和dispose为什么还要用dladdr来判断
        //终于看到flags的用处了
        if (pp11 != 0) {
    
            signature = [NSString stringWithCString:blcok->descriptor->signature encoding:NSUTF8StringEncoding];
    
        } else {
            struct CDBlockDescriptor1 *tmp = blcok->descriptor;
            signature = [NSString stringWithCString:tmp->signature encoding:NSUTF8StringEncoding];
        }
    
        return signature;
    }
    
    
    

    根据以前block总结。可以得出blcok的结构体。
    descriptor目前我遇到两种。
    一个是CDBlockDescriptor1这种,这种是没有捕获变量的情况发。
    另一种是CDBlockDescriptor,捕获了外面的变量

    struct CDBlockDescriptor {
        unsigned long int reserved; //8 offset:0x0
        unsigned long int Block_size;//8  0x8
        void * copy; //8 0x10 copy
        void * dispose;//8 0x18 dispose
        char * signature;//8 0x20  signature
    };
    
    struct CDBlockDescriptor1 {
        unsigned long int reserved; //8 offset:0x0
        unsigned long int Block_size;//8  0x8
        char * signature; //8 0x10 signature
    };
    
    struct CDBlock {
        Class isa; // 0   offset:0x0
        int32_t flags; //4  0x4
        int32_t reserved;//4 0x8
        void *funk;// 8 0x10
        struct CDBlockDescriptor *descriptor;// 8 0x18
        //捕获的变量,....
    
    };
    

    翻译完,终于看到flag的用处了。
    flag为什么要和0x2000000做位与处理,我也不知,汇编代码是这样处理的。0x2000000可能是某个死值吧
    最后打印下:效果几乎和ios13下的打印一样:

    WX20191223-150046@2x.png

    此外,我添加了,偏移量。这样可以用Hopper直接找到函数位置。
    最后给出blcok的所有代码:

    #ifndef CDBlockHeader_h
    #define CDBlockHeader_h
    struct CDBlockDescriptor {
        unsigned long int reserved; //8 offset:0x0
        unsigned long int Block_size;//8  0x8
        void * copy; //8 0x10 copy
        void * dispose;//8 0x18 dispose
        char * signature;//8 0x20  signature
    };
    
    struct CDBlockDescriptor1 {
        unsigned long int reserved; //8 offset:0x0
        unsigned long int Block_size;//8  0x8
        char * signature; //8 0x10 signature
    };
    
    struct CDBlock {
        Class isa; // 0   offset:0x0
        int32_t flags; //4  0x4
        int32_t reserved;//4 0x8
        void *funk;// 8 0x10
        struct CDBlockDescriptor *descriptor;// 8 0x18
        //捕获的变量,....
    
    };
    
    NSString * getBlockDebugDescription(struct CDBlock *blcok);
    NSString* getBlock_signature(struct CDBlock *blcok);
    
    
    NSString * getBlockDebugDescription(struct CDBlock *blcok) {
    
    
        NSMutableString *debugDescription = [NSMutableString stringWithFormat:@"\n<%@: %p>\n",blcok->isa,blcok];
    
        NSString *block_signatur_string = getBlock_signature(blcok);
        [debugDescription appendFormat:@" signature: %@\n",block_signatur_string];
    
        Dl_info invokeInfo = {0};
        if(dladdr(blcok->funk, &invokeInfo) != 0) {
            [debugDescription appendFormat:@" invoke   : %p\n (%s`%s offset:%p)\n",invokeInfo.dli_saddr,invokeInfo.dli_fname,invokeInfo.dli_sname,invokeInfo.dli_saddr - invokeInfo.dli_fbase];
        }
    
        Dl_info copyInfo = {0};
        if(dladdr(blcok->descriptor->copy, &copyInfo) != 0) {
            [debugDescription appendFormat:@" copy     : %p\n (%s`%s offset:%p)\n",copyInfo.dli_saddr,copyInfo.dli_fname,copyInfo.dli_sname,copyInfo.dli_saddr - copyInfo.dli_fbase];
        }
        Dl_info disposeInfo = {0};
        if(dladdr(blcok->descriptor->dispose, &disposeInfo) != 0) {
            [debugDescription appendFormat:@" dispose  : %p\n (%s`%s offset:%p)",disposeInfo.dli_saddr,disposeInfo.dli_fname,disposeInfo.dli_sname,disposeInfo.dli_saddr - disposeInfo.dli_fbase];
        }
        return debugDescription;
    }
    
    NSString* getBlock_signature(struct CDBlock *blcok) {
    
        NSString *signature = nil;
        int32_t pp11 =  0x2000000&blcok->flags; //此处已经判断是哪种descriptor了,
        //CDBlockDescriptor signature 偏移0x20。
        //CDBlockDescriptor1 signature 偏移 0x10
        //不知道在外面在获取copy和dispose为什么还要用dladdr来判断
        //终于看到flags的用处了
        if (pp11 != 0) {
    
            signature = [NSString stringWithCString:blcok->descriptor->signature encoding:NSUTF8StringEncoding];
    
        } else {
            struct CDBlockDescriptor1 *tmp = blcok->descriptor;
            signature = [NSString stringWithCString:tmp->signature encoding:NSUTF8StringEncoding];
        }
    
        return signature;
    }
    
    
    #endif /* CDBlockHeader_h */
    

    测试代码:
    https://github.com/LoveSVN/Block_Struct.git

    相关文章

      网友评论

          本文标题:又是Block小结

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