美文网首页
Block原理

Block原理

作者: 前年的邂逅_Jerry | 来源:发表于2018-03-04 12:20 被阅读19次

    Block就是一个存储了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体。

    一、Block是一个对象。

    #import <objc/runtime.h>
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        int i = 0;
        __unused void(^myBlock)() = ^{
            NSLog(@"block %d",i);
        };
        Class tmpClass = object_getClass(myBlock);
        while (tmpClass) {
            NSLog(@"class %@ superClass %@",tmpClass,class_getSuperclass(tmpClass));
            tmpClass = class_getSuperclass(tmpClass);
        }
    }
    @end
    

    结果:

    class __NSMallocBlock__ superClass __NSMallocBlock
    class __NSMallocBlock superClass NSBlock
    class NSBlock superClass NSObject
    class NSObject superClass (null)
    

    二、Block实现过程

    1、通过命令转换:

    //转换成C++代码命令:xcrun -sdk iphonesimulator8.1 clang -rewrite-objc ViewController.m
    //包含weak类型数据转换成C++:xcrun -sdk iphonesimulator10.2 clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 ViewController.m
    
    - (void)emptyBlockFunction {
        void (^emptyBlock)() = ^{
            NSLog(@"abc");
        };
        
        emptyBlock();
    }
    
    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    
    
    // @implementation BlockStructureViewController
    int globalValue = 10;
    static int staticValue = 5;
    
    
    
    struct __BlockStructureViewController__emptyBlockFunction_block_impl_0 {
      struct __block_impl impl;
      struct __BlockStructureViewController__emptyBlockFunction_block_desc_0* Desc;
      __BlockStructureViewController__emptyBlockFunction_block_impl_0(void *fp, struct __BlockStructureViewController__emptyBlockFunction_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __BlockStructureViewController__emptyBlockFunction_block_func_0(struct __BlockStructureViewController__emptyBlockFunction_block_impl_0 *__cself) {
    
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_fj_03n4zg_x4tv33bsws1sfxlkw0000gn_T_BlockStructureViewController_8a7742_mi_0);
    
        }
    
    static struct __BlockStructureViewController__emptyBlockFunction_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __BlockStructureViewController__emptyBlockFunction_block_desc_0_DATA = { 0, sizeof(struct __BlockStructureViewController__emptyBlockFunction_block_impl_0)};
    
    static void _I_BlockStructureViewController_emptyBlockFunction(BlockStructureViewController * self, SEL _cmd) {
    
        void (*emptyBlock)() = ((void (*)())&__BlockStructureViewController__emptyBlockFunction_block_impl_0((void *)__BlockStructureViewController__emptyBlockFunction_block_func_0, &__BlockStructureViewController__emptyBlockFunction_block_desc_0_DATA));
    
        ((void (*)(__block_impl *))((__block_impl *)emptyBlock)->FuncPtr)((__block_impl *)emptyBlock);
        
        //这里调用结构体的构造函数(函数指针,desc的结构体变量)
        *emptyBlock = &__BlockStructureViewController__emptyBlockFunction_block_impl_0((void *)__BlockStructureViewController__emptyBlockFunction_block_func_0, &__BlockStructureViewController__emptyBlockFunction_block_desc_0_DATA))
        emptyBlock->FuncPtr(emptyBlock);
    }
    

    2、自己实现block

    struct block {
        
        int Flags;
        void *FuncPtr;
        
    };
    
    
    struct block_impl {
        
        struct block impl;
        
        block_impl(void *fp, int flags = 0){
    
            impl.Flags = flags;
            impl.FuncPtr = fp;
            
        }
    };
    static void block_impl_func_0(struct block_impl *__cself) {
        printf("哈哈");
    }
    
    void test() {
    
        struct block_impl blockImpl = block_impl((void *)block_impl_func_0, 20);
        block *tmpBlockPointer = (block *)&blockImpl;
        
        //struct本身是不占内存
        void (*Func)(struct block *) = (void (*)(struct block *))tmpBlockPointer->FuncPtr;
        Func(tmpBlockPointer);
    }
    

    三、__block

    - (void)blockDataBlockFunction {
        __block int a = 100;
        //代码会生成如下
        /*
        struct __Block_byref_a_0 {
          void *__isa;
          struct __Block_byref_a_0 *__forwarding;
          int __flags;
          int __size;
          int a;
         };
        */
        //放到栈里,是系统来控制内存的,离开作用域后,就释放内存了
        //OC里的对象都是在堆区的,堆区的内存需要你自己创建、自己释放
        void (^blockDataBlock)() = ^{        
            //堆栈区
            NSLog(@"block_int_a %p", &a);
            NSLog(@"a = %d", a);        
        };    
      //a->forwarding-a;
        
        //你在栈里的a->forwarding = block里的a的地址
        
        //你刚刚说外面的a是栈里面, block里面的a是在堆里面,那为什么我在外面改变a,能改变里面的值?
        //栈区修改a的值
        a = 10; //此处能在block里面修改a的值 实际上代码里面执行的是a->forwarding.a的值 
        blockDataBlock();
    }
    
        int a = 10;
        void(^block)() = ^{
            NSLog(@"%d",a);
        };
        a = 0;//此处不能修改a的值
        block();
    

    四、block存储的问题

    typedef void(^testBlock)();
    typedef void(^testBlock1)(int a);
    @property(nonatomic, strong)testBlock strongBlock;
    @property(nonatomic, copy)testBlock copyBlock;
    @property(nonatomic, weak)testBlock weakBlock;
    
    @property(nonatomic, strong)testBlock1 strongBlock1;
    @property(nonatomic, copy)testBlock1 copyBlock1;
    @property(nonatomic, weak)testBlock1 weakBlock1;
    

    1、当你没有使用外部变量的时候,通通都是global的,无论你怎么修饰、无论是否成员变量;存储在代码区

        _strongBlock = ^{
            NSLog(@"_strongBlock");
        };
        _copyBlock = ^{
            NSLog(@"_copyBlock");
        };
        _weakBlock = ^{
            NSLog(@"_weakBlock");
        };
        NSLog(@"strongBLock %@  copyBLock %@  weakBLock %@", object_getClass(_strongBlock), object_getClass(_copyBlock), object_getClass(_weakBlock));
    
    结果:
    strongBLock __NSGlobalBlock__  copyBLock __NSGlobalBlock__  weakBLock __NSGlobalBlock__
    

    2、当在block里添加了外部变量,那么block的内存就会变成不一样;

    int k = 0;
        
        _strongBlock = ^{
            NSLog(@"_strongBlock %d", k);
        };
        _copyBlock = ^{
            NSLog(@"_copyBlock %d", k);
        };
        _weakBlock = ^{
            NSLog(@"_weakBlock %d", k);
        };
        
        NSLog(@"strongBLock %@  copyBLock %@  weakBLock %@", object_getClass(_strongBlock), object_getClass(_copyBlock), object_getClass(_weakBlock));
    
    结果:
    strongBLock __NSMallocBlock__  copyBLock __NSMallocBlock__  weakBLock __NSStackBlock__
    

    3、当block赋值给strong变量、(非weak的)成员变量,block为mallocBlock;

        int k = 0;
        _strongBlock1 = ^(int i){
            NSLog(@"_strongBlock1 : i=%d", k);
        };
        _copyBlock1 = ^(int i){
            NSLog(@"_copyBlock1 : i=%d", k);
        };
        _weakBlock1 = ^(int i){
            NSLog(@"_weakBlock1 : i=%d", k);
        };
        NSLog(@"strongBLock1 %@  copyBLock1 %@  weakBLock1 %@", object_getClass(_strongBlock1), object_getClass(_copyBlock1), object_getClass(_weakBlock1));
    
    结果:
    strongBLock1 __NSMallocBlock__  copyBLock1 __NSMallocBlock__  weakBLock1 __NSStackBlock__
    

    4、当block为函数参数,block为stackBlock;

    5、全局变量、静态变量、静态局部变量在block中的存储,block都为globalBlock

    6、static变量无法加__block符的

    7、对globalBlock 进行copy操作 依然是它本身地址 对mallocBlock进行copy,引用计数+1 对stackBlock进行copy 地址改变了

    8、需要手动对block进行copy的情况:作为函数参数,这里涉及到block数组,原因是栈里的对象,你无法来讲引用计数

    • 结论:
    当你的block里有外部变量的时候
    
    当 block 调用 copy 方法时,如果 block 在栈上,会被拷贝到堆上;
    
    当 block 作为函数返回值返回时,编译器自动将 block 作为 _Block_copy 函数,效果等同于 block 直接调用 copy 方法;
    
    当 block 被赋值给 __strong id 类型的对象或 (非weak)成员变量时,编译器自动将 block 作为 _Block_copy 函数,效果等同于 block 直接调用 copy 方法;
    
    当 block 作为参数被传入方法名带有 usingBlock 的 Cocoa Framework 方法或 GCD 的 API 时。这些方法会在内部对传递进来的 block 调用 copy 或 _Block_copy 进行拷贝;
    

    五、循环引用

    typedef void(^cycleBlock)();
    @interface ViewController (){
        cycleBlock blk;
        NSObject *object;
    }
    

    1、例一

        NSObject *otherObject = [[NSObject alloc] init];
        NSLog(@"object %p &object %p", otherObject, &otherObject);
        //otherObject本来是在堆区,然后把block赋值给成员对象,block会自动copy,到堆区里面,otherObject本来在堆区,(没有weak的情况下,就会对它有引用计数+1)
        __weak typeof(otherObject)weakObject = otherObject;
        blk = ^{
            NSLog(@"object %p &object %p", weakObject, &weakObject);
        };
        blk();
        //block 对otherObject不进行引用计数 此操作otherObject被释放掉
        otherObject = nil;
        blk();
        /*
        结果:
      object 0x60400000a630 &object 0x7fff5f32fc98
      object 0x60400000a630 &object 0x600000441160
      object 0x0 &object 0x600000441160
        */
    
        NSObject *otherObject = [[NSObject alloc] init];
        NSLog(@"object %p &object %p", otherObject, &otherObject);
        blk = ^{
            NSLog(@"object %p &object %p", otherObject, &otherObject);
        };
        blk();
        //block 对otherObject进行引用计数 此操作otherObject不会被释放
        otherObject = nil;
        blk();
        /*
        结果:
        object 0x6080000149f0 &object 0x7fff57690c98
        object 0x6080000149f0 &object 0x60800024bb40
        object 0x6080000149f0 &object 0x60800024bb40
        */
    

    2、例二

    __strong讲解: https://www.jianshu.com/p/404e0ea5f6d7

         NSMutableDictionary *otherObject = [[NSMutableDictionary alloc] init];
        NSLog(@"object %p &object %p", otherObject, &otherObject);
        __weak typeof(otherObject)weakObject = otherObject;
        blk = ^{
            __strong typeof(weakObject)strongObject = weakObject;
            NSLog(@"object %p &object %p", strongObject, &strongObject);
            //如果[otherObject setValue:@"sda" forKey:@"12"]; 则产生循环引用
            [strongObject setValue:@"sda" forKey:@"12"];
        };
        
        blk();
        otherObject = nil;
        blk();
        /*
        结果:
        object 0x60c000222480 &object 0x7fff51adcca8
        object 0x60c000222480 &object 0x7fff51adcc08
        object 0x0 &object 0x7fff51adcc08
        */
    

    3、例三,

        NSObject *otherObject = [[NSObject alloc] init];
        NSLog(@"object %p &object %p", otherObject, &otherObject);
        
        __weak typeof(otherObject)weakObject = otherObject;
        
        //只要 block 部分执行了,即使我们中途释放了 obj,block 内部依然会继续强引用它
        
        blk = ^{
            // __strong会保证otherObject到运行结束前都不会被释放
            __strong typeof(weakObject)strongObject = weakObject;
            NSLog(@"object %p &object %p", strongObject, &strongObject);
            sleep(3);
            NSLog(@"object %p &object %p", strongObject, &strongObject);
        };
        
        blk();
        otherObject = nil;
        sleep(5);
        blk();
    /*
    运行结果:
    object 0x608000010780 &object 0x7fff59c14ca8
    object 0x608000010780 &object 0x7fff59c14c08
    object 0x608000010780 &object 0x7fff59c14c08
    object 0x0 &object 0x7fff59c14c08
    object 0x0 &object 0x7fff59c14c08
    */
    

    4、例四,对成员变量的循环引用

    - (void)strongCycleBlockFunction {
        
        object = [[NSObject alloc] init];
        NSLog(@"object %p &object %p", object, &object);
    
        //对于成员变量,block是把self引用计数+1,不是对成员变量object本身来增加引用计数的
        blk = ^{
            
            NSLog(@"object %p &object %p", object, &object);
            
        };
        blk();
        //这里的object和block中的block是同一个内存地址
        object = nil;
        blk();
      /*
    结果:因为block不是对objec进行引用的,而是对self进行引用
     object 0x600000010180 &object 0x7fd7426061e8
     object 0x600000010180 &object 0x7fd7426061e8
     object 0x0 &object 0x7fd7426061e8
      */
    }
    

    5、例五、block里面定义静态变量或者使用外部的静态变量

    不需要对静态变量进行任何修饰,不存在循环引用。

    static int i = 0;
    @interface SecondController ()
    
    @end
    
    @implementation SecondController
    - (void)dealloc{
        NSLog(@"%s",__func__);
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        void (^test)() = ^{
            static int j = 0;
            NSLog(@"i = %d, j = %d", i,j);
        };
        i = 2;
        test();
    }
    

    6、例六

    @interface SecondController ()
    @property (nonatomic, copy) void(^blk)(NSString * str);
    @end
    
    @implementation SecondController{
        NSString * str;
    }
    - (void)dealloc{
        NSLog(@"%s",__func__);
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        str = @"sjdk";
        self.view.backgroundColor = [UIColor whiteColor];
        self.blk = ^(NSString * str2){
            //self 持有 block ,block 持有str ,str被self持有,这里会产生循环引用。
            str = str2;
            NSLog(@"%@",str);
        };
        self.blk(@"21");
    }
    @end
    

    如下写法是不存在循环引用的

    @interface SecondController ()
    
    @end
    
    @implementation SecondController{
        NSString * str;
    }
    - (void)dealloc{
        NSLog(@"%s",__func__);
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        str = @"sjdk";
        self.view.backgroundColor = [UIColor whiteColor];
        void(^blk)(NSString *) = ^(NSString * str2){
            str = str2;
            NSLog(@"%@",str);
        };
        blk(@"21");
    }
    @end
    

    相关文章

      网友评论

          本文标题:Block原理

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