美文网首页
Block底层实现

Block底层实现

作者: 哈哈哈我的简书账号 | 来源:发表于2017-03-28 21:07 被阅读20次

    《objective-c高级编程》这本书是一本非常好的书,但是时间比较早,书中有一些理论不适合最新的ios版本,并且书中论述ARC和MRC的情况比较混乱,穿插讲,但丝毫不会掩饰这本书的好。瑕不掩瑜

    iOS代码块Block

    概述

    代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中,常用于GCD、动画、排序及各类回调

    Block变量的声明、赋值与调用

    Block变量的声明

    block和普通的变量一样可以作为属性,参数,返回值

    作为属性时的声明如下:

    形式为: 返回值类型 (^block名称) (参数声明);

    int (^blockName) (int);
    void (^blockName) (void);
    void (^blockName) ();
    

    typedef形式如下:

    typedef int (^blk_t)();
    
    blk_t blockName;
    
    
    作为函数参数时的声明如下:

    objective-c形式

    - (id) fun: (int (^)())block
    {
        int var = 10;
        NSArray *arry = [[NSArray alloc] initWithObjects:^{NSLog(@"%d",var);}, nil];
        return arry;
    }
    

    typedef形式如下:

    typedef int (^blk_t)();
    
    - (id) fun: (blk_t)block
    {
        int var = 10;
        NSArray *arry = [[NSArray alloc] initWithObjects:^{NSLog(@"%d",var);}, nil];
        return arry;
    }
    
    

    c语言形式如下:

    void func (int (^block)())
    {
        
    }
    
    
    typedef int (^blk_t)();
    
    void func (blk_t  block)
    {
        
    }
    
    作为返回值类型时

    objective-c形式

    - (int (^)()) funoc
    {
        return ^(int count){return count;};
    }
    

    typedef形式如下:

    typedef int (^blk_t)();
    
    - (blk_t) funoc
    {
        return ^(int count){return count;};
    }
    
    

    c语言形式如下:

    int (^fun())()
    {
        return ^(int count){return count;};
    }
    

    typedef形式如下:

    typedef int (^blk_t)();
    
    blk_t fun()
    {
        return ^(int count){return count;};
    }
    
    

    Block变量的定义

    ^ 返回值类型 (参数) {};
    说明:返回值类型可以省略,(参数)可以省略,最简形式为^{};

    //完整形式
        ^ int (int count){return count;};//完整形式
        
        //省略返回值
        ^  (int count){return count;};//返回值类型可以省略,默认与返回值类型相同
        
        //省略参数
        ^ {
        //方法体
        };//如果没有参数,可以省略参数
      
     //没有省略参数的形式
       ^ (void){
          //方法体
        };//参数为空可以省略参数
    

    Block变量的调用

    block的调用很简单,直接在block名称加括号,与函数调用一致

        int (^blockName)(int) = ^ int (int count){return count;};//完整形式
        NSLog(@"%d",blockName(10));
    
    MRC

    mrc下捕获自动变量的block会被存储在栈区,调用copy可以被复制到堆上

    但是以下不需要主动复制

    • 调用copy
    • 传递给usingBlock方法
    • GCD函数的参数
    ARC

    arc下捕获自动变量的block会从栈区复制到堆区,这是编译器默认做的
    但并不是说,ARC下不存在栈区的block;如果栈上的block没有赋值给

    • strong类型属性,
    • copy类型属性,
    • __strong类型的实例变量,
    • 作为函数返回值,
    • 传递给函数作为参数,
      那么block不会自动复制到堆上

    截获自动变量

    首先我们看下变量所有类型

    • 自动变量(局部变量)
    • 函数参数
    • 静态变量
    • 静态全局变量
    • 全局变量

    block对自动变量的截获如下:

    int var = 10;
    void (^blockVar)() = ^{
        NSLog(@"%d",var);
    };
    blockVar();
    

    但是不能对截获的自动变量修改

    NSMutableArray *array = [NSMutableArray array];
    void (^blockVar)() = ^{
        [array addObject:[[NSObject alloc] init]];//正确,因为没有对array赋值,只是向array添加数据
         array = [NSMutableArray array];//错误,改变了array的数值
    };
    

    以上代码中,没有对array的值修改,是可以的
    但是不能访问c语言数组类型,即不能捕获c语言数组

        char c[10] = {'q','q'};
        void (^blockVar)() = ^{
            c[0];//编译报错
        };
    

    但是c语言数组,我们可以通过指针的方式访问

    char *q = @"ustb";
    void (^blockVar)() = ^{
        q[0];//合法
    };
    

    截获自动变量是值拷贝,在由block编译生成的结构体中,声明与自动变量相对应的成员变量来存放截获的自动变量值,用于存放捕获的自动变量的值
    MRC下编译器不会默认地把block拷贝到堆区,需要手动调用copy;ARC下,如果赋值给强指针会默认把block拷贝到堆区

    截获对象

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    typedef void (^blk_t)(id);
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
            blk_t blk;
            {
                id arrayM = [NSMutableArray array];
                blk = ^(id obj){
                    [arrayM addObject:obj];
                    NSLog(@"%ld",[arrayM count]);
                };//arc环境下默认会拷贝到堆
            }
            
            blk([[NSObject alloc] init]);
            
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    80A72DAA-A3EB-4939-98F8-7D2363273643.png

    我们可以看到在堆上的block对截获的对象会有retain操作,并不会在对象作用域结束时被释放
    我们再来看下在栈上的block会怎么样

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "TestObject.h"
    extern int  _objc_rootRetainCount(id);
    typedef void (^blk_t)(id);
    int main(int argc, char * argv[]) {
        @autoreleasepool {
    
            __unsafe_unretained void (^block)(id);
            
            id array = [NSMutableArray array];
            block = ^(id obj){
                [array addObject:obj];//不会retain array
                NSLog(@"%@",array);
            };
            [array release];
            
            block([[NSObject alloc] init]);//EXC_BAD_ACCESS
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

    我们看一下示例代码

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "TestObject.h"
    extern int  _objc_rootRetainCount(id);
    typedef void (^blk_t)(id);
    int main(int argc, char * argv[]) {
        @autoreleasepool {
    
            __unsafe_unretained void (^block)(id);
            
            {
                id array = [NSMutableArray array];
                block = ^(id obj){
                    [array addObject:obj];//MRC下EXC_BAD_ACCESS,ARC下array为nil
                    NSLog(@"%@",array);
                };//无论MRC,ARC,block都在栈上
    //            [array release];
            }
            
            block([[NSObject alloc] init]);//不管MRC,ARC都没有来的急释放,按理说超出作用域,栈上的对象会被回收的
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    

    可以看出栈上的block捕获的对象,相应的成员变量不会强引用被捕获的对象,但是当对象被释放的时候,如果是ARC环境,那么相应的成员变量会被负值nil;如果是MRC,不会负值nil,这时如果访问,会报EXC_BAD_ACCESS错误

    截获对象且用__block修饰

    __block修饰对象在mrc和ARC下不同,在ARC下会增加对象的引用计数,mrc不会增加引用计数,因此在mrc下可以用来防止引用计数

    IMG_0262.JPG

    ARC下防止引用循环有两种方式,__block,__weak

    #import "ViewController.h"
    @interface ViewController ()
    @property (nonatomic, strong) void (^t)();
    @end
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
         __block  ViewController *strongSelf = self;
        _t = [^{
            NSLog(@"%@",Sself);
            strongSelf = nil;//付空,打破引用循环
        } copy];
    }
    - (void)dealloc
    {
        NSLog(@"view dealloc!!!!!");
    }
    @end
    
    #import "ViewController.h"
    @interface ViewController ()
    @property (nonatomic, strong) void (^t)();
    @end
    
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        __weak  ViewController *weakSelf = self;
        _t = [^{
            NSLog(@"%@",weakSelf);
        } copy];
    }
    - (void)dealloc
    {
        NSLog(@"view dealloc!!!!!");
    }
    @end
    

    MRC下使用 __block 防止引用循环

    #import "ViewController.h"
    @interface ViewController ()
    @property (nonatomic, copy) void (^t)();
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
         __block  ViewController *WeakSelf = self;
        _t = [^{
            NSLog(@"%@",WeakSelf);//__block在MRC下的作用是防止循环引用,不会增加引用计数,这和ARC不同(这在官方文档Transitioning to ARC Release Notes有详细解释)
        } copy];
    }
    - (void)dealloc
    {
        [super dealloc];
        [_t release];
        NSLog(@"view dealloc!!!!!");
    
    }
    @end
    

    截获自动变量且用__block 修饰

    __block修饰变量,会把变量作为成员变量放到结构体里,如果使用__block变量的block被拷贝到堆上,那么__block变量也会被拷贝到堆上

    循环引用

    对于循环引用的问题,mrc下通过__block解决,arc下通过__weak解决,或者在block执行完时手动置相应指针nil打破循环引用,__weak和__block不能同时使用

    Block的实现

    block的实质

    进入项目目录,然后我们可以通过 clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxx.m指令将xxx.m文件编译成.cpp文件

    //objective-c
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
            void (^blk)() = ^{printf("Block\n");};
            blk();
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    

    转换C++代码如下:

    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;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;//在栈上,但是打印出来是__NSGlobalBlock__,按理说应该就是应该在全局区的,这个地方编译的时候可能有误差
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    printf("Block\n");}
    
    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, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
            ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    

    我们看到原先的代码块,编译之后,变成了结构体
    命名规则是根据block所属函数(main函数)和block在函数中出现(此处0)的位置命名的

    我们看到结构体中含有isa指针,说明这是一个objective-c对象
    所以我们说block是objective-c对象

    截获自动变量值的实质

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            int var = 10;
            const char *fmt = "var = %d\n";
            void (^blk)() = ^{printf(fmt,var);};
            var = 2;
            fmt = "These values changed. var = %d\n";
            blk();
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    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;
      const char *fmt;
      int var;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _var, int flags=0) : fmt(_fmt), var(_var) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      const char *fmt = __cself->fmt; // bound by copy
      int var = __cself->var; // bound by copy
    printf(fmt,var);}
    
    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, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            int var = 10;
            const char *fmt = "var = %d\n";
            void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, var));
            var = 2;
            fmt = "These values changed. var = %d\n";
            ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    

    我们看到捕获的自动变量被作为成员变量添加到__main_block_impl_0中啦,结构体如下

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      const char *fmt;
      int var;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _var, int flags=0) : fmt(_fmt), var(_var) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    在初始化结构体时,根据传递给构造函数的参数对由自动变量添加的成员变量初始化
    我们可以看到,这仅仅是值传递,所以在block的外部修改自动变量不会影响block内的值,因此block也禁止在block内对相应的成员变量修改
    截获自动变量的意思指:声明block语法时,block内使用的自动变量被保存在block内的结构体中

    __block说明符

    如果想在block中修改截获的自动变量的值,有两种方式,第一c语言有一个变量允许在block内写值,

    • 静态变量
    • 静态全局变量
    • 全局变量
      虽然block的匿名函数部分简单的转换成c语言的函数,但从这个变换的函数中访问静态变量,全局变量没有改变
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    static int intS;
    int intE;
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            int var = 10;
            static int inlineInt = 10;
            const char *fmt = "var = %d\n";
            static NSString *str = @"ustb";
            void (^blk)() = ^{printf(fmt,var);
                intS = 10;
                intE = 10;
                inlineInt = 100;
                str = @"value changed ustb";
            };
            var = 2;
            fmt = "These values changed. var = %d\n";
            blk();
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    
    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    
    static int intS;
    int intE;
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      const char *fmt;
      int var;
      int *inlineInt;
      NSString **str;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _var, int *_inlineInt, NSString **_str, int flags=0) : fmt(_fmt), var(_var), inlineInt(_inlineInt), str(_str) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      const char *fmt = __cself->fmt; // bound by copy
      int var = __cself->var; // bound by copy
      int *inlineInt = __cself->inlineInt; // bound by copy
      NSString **str = __cself->str; // bound by copy
                printf(fmt,var);
                intS = 10;
                intE = 10;
                (*inlineInt) = 100;
                (*str) = (NSString *)&__NSConstantStringImpl__var_folders_wh_ycjfh9y54fbg8172sbbfcsbc0000gp_T_main_a2e925_mi_1;
            }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 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; 
            int var = 10;
            static int inlineInt = 10;
            const char *fmt = "var = %d\n";
            static NSString *str = (NSString *)&__NSConstantStringImpl__var_folders_wh_ycjfh9y54fbg8172sbbfcsbc0000gp_T_main_a2e925_mi_0;
            void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, var, &inlineInt, &str, 570425344));
            var = 2;
            fmt = "These values changed. var = %d\n";
            ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    
    

    我们可以看到全局变量和静态全局变量并没有加入到block结构体中,静态局部变量,会以指针的形式访问
    个人理解:自动变量在超出作用域后会被释放,所以block内存放的是值拷贝,不可修改,但是全局变量和静态变量,并不会被销毁所以我们可以在block内公用一份复本,可以修改,特别的,静态全局变量超出作用域后无法直接访问,所以用block内添加指针成员变量,指向静态局部变量
    第二使用__block修饰符
    __block说明符像static,auto,register一样,用于说明变量存放在哪个地方

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            __block int var = 10;
            void (^blk)() = ^{
                var = 1;
            };
            blk();
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    
    
    
    
    struct __Block_byref_var_0 {
      void *__isa;
    __Block_byref_var_0 *__forwarding;
     int __flags;
     int __size;
     int var;
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_var_0 *var; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__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_var_0 *var = __cself->var; // bound by ref
    
                (var->__forwarding->var) = 1;
            }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var, 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_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 10};
            void (*blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_0 *)&var, 570425344));
            ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    

    我们可以看到多了结构

    struct __Block_byref_var_0 {
      void *__isa;
    __Block_byref_var_0 *__forwarding;
     int __flags;
     int __size;
     int var;
    };
    
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    

    这就是说__block修饰的变量作为成员变量被放在结构体里,我们称被__block修饰的变量叫__block变量

    BLOCK存储域

    block分三个存储区:

    • 全局区(_NSConcreteGlobalBlock)
    • 栈区(_NSConcreteStackBlock)
    • 堆区(_NSConcreteMallocBlock)

    除了全局变量,静态变量,静态全局变量外没有捕获其他自动变量的block会被存放在全局区,结构体isa = &_NSConcreteGlobalBlock;

    对栈上的对象retain是不管用的

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "TestObject.h"
    extern int  _objc_rootRetainCount(id);
    typedef void (^blk_t)(id);
    int main(int argc, char * argv[]) {
       @autoreleasepool {
    
           int var = 10;
           void (^b)() = ^{
               printf("%d\n",var);
           };
           NSLog(@"%@",b);
           NSLog(@"%d",_objc_rootRetainCount(b));
           [b retain];
           NSLog(@"%@",b);
           NSLog(@"%d",_objc_rootRetainCount(b));
           [b release];
           NSLog(@"%@",b);
           NSLog(@"%d",_objc_rootRetainCount(b));
           b();
           
           return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
       }
    }
    
    D70F2667-B6D0-4855-8313-4C5A85173409.png

    习题:

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "TestObject.h"
    extern int  _objc_rootRetainCount(id);
    typedef void (^blk_t)(id);
    int main(int argc, char * argv[]) {
        @autoreleasepool {
    
            void (^block)(id);
            
            TestObject *obj = [[TestObject alloc] init];
            block = [^(id obj){
                
                NSLog(@"%@",obj);
            } copy];//TestObject类型的obj对象没有retain操作
            block(obj);
            [obj release];//调用完,obj被释放
            [block release];
            
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

    对比:

    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "TestObject.h"
    extern int  _objc_rootRetainCount(id);
    typedef void (^blk_t)(id);
    int main(int argc, char * argv[]) {
        @autoreleasepool {
    
            void (^block)();
            
            TestObject *obj = [[TestObject alloc] init];
            block = [^{
                
                NSLog(@"%@",obj);
            } copy];//TestObject类型的obj对象有retain操作
            [obj release];//调用完,obj不会被释放
            [block release];//调用完,obj会被释放
            
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

    修饰符问题:
    ARC下:
    void (^blk)();//默认强引用
    __weak void (^blk)(); //blk编译为指向结构体的指针,类型为weak类型,修饰的是blk,block不会复制到堆上
    __block void (^blk)();//blk会作为结构体成员变量
    截获__block变量,其实截获blk所在结构体的指针,blk是strong类型

    相关文章

      网友评论

          本文标题:Block底层实现

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