iOS基础知识

Block对象变量捕获(三)

2021-09-01  本文已影响0人  鄂北

前面已经说过Block对基本类型变量的捕获,现在来看看Block对对象变量的捕获。先看看下面几个问题

问题一

        Person * person = [[Person alloc] init];
        person.age = 10;
        void (^block)(void) = ^{
            NSLog(@"年龄:%ld",person.age);
        };
        person.age = 20;
        block();
        
        NSLog(@"%@",[block class]);

问题:

  • 1、输出的年龄是多少?
  • 2、block类型是什么?

答案:

  • 1、输出的年龄为20
  • 2、NSMallocBlock

1、对象的局部变量捕获和基本数据类型有点区别,对象的局部auto变量捕获是指针捕获不是值捕获,所以捕获的内容会受外部变量的影响
2、因为block捕获了auto变量所以block是NSStackBlock,在ARC下将block赋值给__strong指针时会自动将栈上的block复制到堆上,block由NSStackBlock变为NSMallocBlock

问题二

typedef void(^JTblock)(void);
        JTblock block;
        {
            Person * person = [[Person alloc] init];
            block = ^{
                NSLog(@"%@",person);
            };
        }
        block();

问题:

  • 1、person什么时候被释放
  • 2、block类型是什么?

答案:

  • 1、 在执行完block()后被释放
  • 2、NSMallocBlock

1、这里Person虽然是局部变量,但是不是在离开作用域时(离开大括号)被释放,因为Person在被block捕获时被block强引用了,引用计数加了1,只有当引用计数为0时才会被释放。当block调用完从堆上移除时,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的auto变量(release),person的引用计数减1,此时person的引用计数为0被释放
2、block为NSMallocBlock的原因与问题一一样,这里需要说的是,只有在NSMallocBlock下才会对auto变量产生强引用,如果block是在栈上,将不会对auto变量产生强引用。
如果block被拷贝到堆上,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

问题三

        JTblock block;
        {
            Person * person = [[Person alloc] init];
            __weak Person * weakPerson = person;
            block = ^{
                NSLog(@"%@",weakPerson);
            };
        }
        block();

问题:

  • Person什么时候被释放

答案:

  • 离开大括号作用域时被释放
3-1.png

在问题二中有说到,堆上的Block会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。这里的person的修饰符为weak,所以block会对其产生弱引用,引用计数不会加1,当出作用域时person会被释放

问题四

- (void)captureAutoVariable
{
    self.age = 10;
    void (^captureBlock)(void) = ^{
        NSLog(@"age is %ld",self.age);
    };
    self.age = 20;
    captureBlock();
}

问题:

  • 输出的age是多少

答案:

  • 20

1、self为对象局部auto变量,所以是指针捕获,内部的值会受外部的影响
2、block对self产生了一个强引用

static void _I_Person_captureAutoVariable(Person * self, SEL _cmd) {
    ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("setAge:"), (NSInteger)10);
    void (*captureBlock)(void) = ((void (*)())&__Person__captureAutoVariable_block_impl_0((void *)__Person__captureAutoVariable_block_func_0, &__Person__captureAutoVariable_block_desc_0_DATA, self, 570425344));
    ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("setAge:"), (NSInteger)20);
    ((void (*)(__block_impl *))((__block_impl *)captureBlock)->FuncPtr)((__block_impl *)captureBlock);
}

Block变量捕获详解(一)
Block的三种类型(二)
__block修饰符(四)
Block循环引用(五)

上一篇 下一篇

猜你喜欢

热点阅读