zoukankan      html  css  js  c++  java
  • Object-C语言Block的实现方式

    开场白
    Block基本概念
    中间态转换方法
    Block编译后结果分析
    Block运行时状态与编译状态对比
     
    开场白
     
    Object-C语言是对C语言的扩展,所以将OC源码进行编译的时候,会将OC源码会被转换成CC++,所以想了解OC源码的实现细节,还是需要手动编译成中间状态进行观察。
    命令1:
    clang -rewrite-objc main.m

    如果Xcode版本较高,可能会出现报错:

    ./block_VC.h:9:9: fatal error: 'UIKit/UIKit.h' file not found

    此时可尝试另一个命令:

    clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m
    命令好长,每次输入这么长的指令很麻烦,此时可以考虑使用命令别名:alias

    Block的基本概念

    _NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
    _NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
    _NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。
    堆中的 block何时转换:
    1.调用copy方法时
    2.从一个函数返回时
    3.将block传入dispatch等系统IPA参数block或者传递给带有usingBlock的Cocoa框架函数时。
    4.将block赋给带有__strong修饰符的id类型或者Block类型时。
    Block类型变换方式

    1.没有捕获任何局部变量的block为_NSConcreteGlobalBlock,它以static函数的形式存储在代码区

    2.捕获了局部变量的block为_NSConcreteStackBlock

    3.当_NSConcreteStackBlock出现上面四种情况时,会变成_NSConcreteMallocBlock。(注意此时是调用了Block_Copy函数后)

    Block编译后结果分析

    基本类型变量

    OC源码:

    NSInteger age = 10;
    void(^completeBlock)(NSString *) = ^(NSString *name) {
        NSString *info = [NSString stringWithFormat:@"name:%@ - age:%d",name,age];
        NSLog(@"%@",info);
    };
    
    completeBlock(@"jack");

    编译后的CC++源码:

    // __block_impl为block的基础struct
    //1.*isa:当前block对象所属的类型(注意:在OC语言中带有isa指针的都是对象)
    //2.Flags:标示位,当类型为NSConcreteMallocBlock时,表示需要ARC内存管理,其他默认不需要内存管理
    //3.Reserved:预留标示位,暂时不管它
    //4.block的实际static内部函数地址指针
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    // __main_block_impl_0为block的最终struct
    //1.impl:为上面的__block_impl结构体实例
    //2.Desc:为block的描述结构体__main_block_desc_0,里面包含了block结构体的信息描述,在下面讲解
    //3.age:从外面补货的变量,block采用的方式是直接在block实例中定义一个变量,将捕获的值赋给它
    //4.__main_block_impl_0:为block结构体的构造函数,参数中包含从外面捕获的变量值
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        NSInteger age;
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger _age, int flags=0) : age(_age) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    
    //__main_block_func_0为block具体实现的函数载体,具体的block的任务实现在此函数内
    //参数:1是block的实例指针,2是block的外部传参name
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
        //从block实例中获取age变量的值
        NSInteger age = __cself->age; // bound by copy
    
        //生成字符串NSString *info = [NSString stringWithFormat:@"name:%@ - age:%d",name,age];
        NSString *info = ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_83d55e_mi_0, (NSString *)name, (NSInteger)age);
        
        //打印NSLog(@"%@",info);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_83d55e_mi_1,info);
    }
    
    //reserved:保留字0
    //Block_size:block实例在内存中占据内存大小
    //__main_block_desc_0_DATA:默认desc结构体实例
    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)};
    
    //main函数
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
    
            NSInteger age = 10;
            void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
    
            ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_83d55e_mi_2);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }

    block简单结构布局

    struct __block_impl
    struct __main_block_impl_0
    static void __main_block_func_0
    static struct __main_block_desc_0
    详细解释见上面的中间编译状态。
    block实例中定义了一个age变量,并将外部的age传了过去
     
    __block基本类型变量
    OC源码:
    __block NSInteger age = 10;
    void(^completeBlock)(NSString *) = ^(NSString *name) {
        NSString *info = [NSString stringWithFormat:@"name:%@ - age:%d",name,age];
        NSLog(@"%@",info);
    };
    
    completeBlock(@"jack");
    编译后的CC++源码:
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    //age的block变量实例__Block_byref_age
    //__isa:age的block变量实例所属的类型
    //__forwarding:age的block变量实例的指针
    //__flags:变量是否被内存管理标示
    //__size:所占内存大小
    //age:age变量值
    struct __Block_byref_age_0 {
        void *__isa;
        __Block_byref_age_0 *__forwarding;
        int __flags;
        int __size;
        NSInteger age;
    };
    
    //原来的age变量值,变成了__Block_byref_age_0变量指针
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        __Block_byref_age_0 *age; // by ref
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : 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, NSString *name) {
        //通过block实例获取变量的指针
        __Block_byref_age_0 *age = __cself->age; // bound by ref
    
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_7acc63_mi_0,name,(age->__forwarding->age));
    }
    
    //当block从栈复制到堆上时,会对__Block_byref_age_0变量进行拷贝,也会从栈拷贝到堆上
    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_byref_age_0变量的析构函数,当block实例的引用计数为0时,是否__Block_byref_age_0变量的内存空间
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    //desc结构体中新增了copy函数和dispose函数指针
    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};
    
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
            //block变量实例
            __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
            //block实例
            void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
    
            ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_7acc63_mi_1);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }

    简单结构布局

    struct __block_impl
    struct __main_block_impl_0
    static void __main_block_func_0
    static struct __main_block_desc_0
    //新增
    struct __Block_byref_age_0
    static void __main_block_copy_0(
    static void __main_block_dispose_0(

    __block NSInteger age = 10;后,在简单布局结构中新增了3项

    struct __Block_byref_age_0
    static void __main_block_copy_0(
    static void __main_block_dispose_0(
    __Block_byref_age_0将基本变量转换成__block变量结构体实例指针
    __main_block_copy_0和__main_block_dispose_0是对__block变量结构体实例的内存管理方法。
    __block变量结构体实例从栈拷贝到堆时,调用方法:__main_block_copy_0
    __block变量结构体实例引用计数为0时,调用方法:__main_block_dispose_0
     
    指针类型变量

    OC源码:

    NSMutableArray *friends = [NSMutableArray array];;
    void(^completeBlock)(NSString *) = ^(NSString *name) {
        NSLog(@"%@--%@",name,friends);
    };
    
    completeBlock(@"jack");

    编译后的CC++源码:

    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;
        NSMutableArray *friends;
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableArray *_friends, int flags=0) : friends(_friends) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
        NSMutableArray *friends = __cself->friends; // bound by copy
    
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_1b4a2f_mi_0,name,friends);
    }
    //block实例从栈复制到堆上时,将指针变量引用计数加一
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->friends, (void*)src->friends, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    //block实例引用计数为0时,析构指针变量对象
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->friends, 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};
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
    
            NSMutableArray *friends = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));;
            void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, friends, 570425344));
    
            ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_1b4a2f_mi_1);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }

    简单结构布局

    struct __block_impl
    struct __main_block_impl_0
    static void __main_block_func_0
    static struct __main_block_desc_0
    static void __main_block_copy_0(
    static void __main_block_dispose_0(
    block实例中定义了一个NSMutableArray *friends;指针变量,并通过构造函数将指针传递给block实例
    __main_block_copy_0:block实例从栈复制到堆上时,将指针变量引用计数加一
    __main_block_dispose_0:block实例引用计数为0时,析构指针变量对象

    __block指针类型变量

    OC源码:

    __block NSMutableArray *friends = [NSMutableArray array];;
    void(^completeBlock)(NSString *) = ^(NSString *name) {
        NSLog(@"%@--%@",name,friends);
    };
    
    completeBlock(@"jack");

    编译后的CC++源码:

    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    
    //__Block_byref_friends_0结构体实例中包含了friends指针
    //并且多了__Block_byref_id_object_copy函数:对friends进行copy
    //多了__Block_byref_id_object_dispose函数:对friends进行析构
    struct __Block_byref_friends_0 {
        void *__isa;
        __Block_byref_friends_0 *__forwarding;
        int __flags;
        int __size;
        void (*__Block_byref_id_object_copy)(void*, void*);
        void (*__Block_byref_id_object_dispose)(void*);
        NSMutableArray *friends;
    };
    
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        __Block_byref_friends_0 *friends; // by ref
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_friends_0 *_friends, int flags=0) : friends(_friends->__forwarding) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself, NSString *name) {
        __Block_byref_friends_0 *friends = __cself->friends; // bound by ref
    
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_252d0c_mi_0,name,(friends->__forwarding->friends));
    }
    
    //block实例从栈复制到堆上时,__block变量结构体实例将指针变量引用计数加一,同时__block变量结构体实例内的NSMutableArray *friends变量也递归加一
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->friends, (void*)src->friends, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    //block实例引用计数为0时,__block变量结构体实例被析构,同时__block变量结构体实例内的NSMutableArray *friends变量也递归析构
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->friends, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    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};
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
    
            __attribute__((__blocks__(byref))) __Block_byref_friends_0 friends = {(void*)0,(__Block_byref_friends_0 *)&friends, 33554432, sizeof(__Block_byref_friends_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"))};;
            void(*completeBlock)(NSString *) = ((void (*)(NSString *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_friends_0 *)&friends, 570425344));
    
            ((void (*)(__block_impl *, NSString *))((__block_impl *)completeBlock)->FuncPtr)((__block_impl *)completeBlock, (NSString *)&__NSConstantStringImpl__var_folders_4y_ks8945f50k51_0j95ytw7ss80000gn_T_main_252d0c_mi_1);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }

    简单结构变量

    struct __block_impl
    struct __main_block_impl_0
    static void __main_block_func_0
    static struct __main_block_desc_0
    static void __main_block_copy_0(
    static void __main_block_dispose_0(
    //新增
    struct __Block_byref_age_0
    和__block普通变量一样,出现了__Block_byref_age_0结构体
    __Block_byref_friends_0结构体实例中包含了friends指针
    并且多了__Block_byref_id_object_copy函数:对friends进行copy
    多了__Block_byref_id_object_dispose函数:对friends进行析构
    同时在函数__main_block_copy_0和__main_block_dispose_0中,出现的也是对__Block_byref_age_0结构体的调用。它是在将__Block_byref_age_0结构体析构之前,先析构friends对象。
     
    Block编译中间态与运行时状态对比
    利用lldb打印出来的结果
    局部自由变量
    //局部变量
    __block NSMutableArray *friends = [NSMutableArray array];;
    void(^completeBlock)(NSString *) = ^(NSString *name) {
        NSLog(@"%@--%@",name,friends);
    };
    
    
    (lldb) expression -P 5 -- completeBlock
    (void (^)(NSString *)) $2 = 0x000000010a061130 {
      __isa = __NSMallocBlock__
      __flags = -1023410170
      __reserved = 0
      __FuncPtr = 0x000000010a061130 (iOS_KnowledgeStructure`__main_block_invoke at main.m:19) {}
    }

    利用clang转换后的CC++源码

    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        __Block_byref_friends_0 *friends; // by ref
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_friends_0 *_friends, int flags=0) : friends(_friends->__forwarding) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    证明clang编译器和llvm编译器编译后的结果有出入。发现isa指针不一致。
    编译后的为:impl.isa = &_NSConcreteStackBlock;
    运行时的为:__isa = __NSMallocBlock__
    原因是block类型转换情况4:“将block赋给带有__strong修饰符的id类型或者Block类型时。”
     
    注意:
    _NSConcreteMallocBlock一般不会在源码中出现,它通常在block copy到堆时出现,变化的源码:
    static void *_Block_copy_internal(const void *arg, const int flags) {
    struct Block_layout *aBlock;
    const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
    // 1
    if (!arg) return NULL;
    // 2
    aBlock = (struct Block_layout *)arg;
    // 3
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
    // latches on high
    latching_incr_int(&aBlock->flags);
    return aBlock;
    }
    // 4
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
    return aBlock;
    }
    // 5
    struct Block_layout *result = malloc(aBlock->descriptor->size);
    if (!result) return (void *)0;
    // 6
    memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    // 7
    result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
    result->flags |= BLOCK_NEEDS_FREE | 1;
    // 8
    result->isa = _NSConcreteMallocBlock;
    // 9
    if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
    (*aBlock->descriptor->copy)(result, aBlock); // do fixup
    }
    return result;
    }
     
  • 相关阅读:
    Sencha, the nightmare!
    最近这一年
    SharePoint 是哪些人设计、开发的?
    用 Excel 测试“绘制两点间连线”的算法
    实现一个基于 SharePoint 2013 的 Timecard 应用(下)
    实现一个基于 SharePoint 2013 的 Timecard 应用(中)
    实现一个基于 SharePoint 2013 的 Timecard 应用(上)
    ASP.NET 在 Windows Azure 环境中使用基于 SQLServer 的 Session
    SQLServer 查询使用键查找时锁申请及释放顺序
    由delete导致的超时已过期问题
  • 原文地址:https://www.cnblogs.com/zhou--fei/p/10224049.html
Copyright © 2011-2022 走看看