ios中的block

来源:互联网 发布:淘宝领券怎么领取 编辑:程序博客网 时间:2024/06/01 08:13
1.block的实质
Block的实质就是一个结构体
我们可以通过clang -rewrite-objc .m文件将oc的.m文件转换为c++的.cpp文件,来查看block的实质
例:
int main(int argc, const char * argv[]) {    @autoreleasepool {        //1.默认情况下block会将外部变量先copy一份,考虑多线程问题        int a = 10;        void (^op)() = ^{            NSLog(@"%d",a);//此时输出的是10,因为这里的a被copy了一份        };        a  = 100;        op();    }    return 0;}






打开main.cpp



2.block存放位置和使用copy
因为block实质上是一个结构体,所以block默认是放到栈中,所以默认情况下block的生命周期比较短,会被自动释放,如果想让block不被自动释放,则需要将block放到堆中,而只有copy才能使block代码拷贝到堆中,其他修饰都不能,所以我们在声明block成员变量时应该使用copy,保证类的实例对象在,则block也在
例:
#import <Foundation/Foundation.h>@interface Person : NSObject@property (nonatomic, copy) void (^operation)();@end


3.block线程安全
Block是线程安全的,他会将外部访问变量进行拷贝一份,避免被无意中修改
例:
例:
int main(int argc, const char * argv[]) {    @autoreleasepool {        int a = 10;        void (^block)() = ^{            NSLog(@"%d",a);//这里a会被拷贝一份        };        //这里a被修改为100了,但是block中的a已不再是外部的a了,他已经被拷贝了一份        a = 100;        block();    }    return 0;}



而使用__block后,会禁止block拷贝
例:
int main(int argc, const char * argv[]) {    @autoreleasepool {        __block int a = 10;//这里表明a变量不会被拷贝        void (^block)() = ^{            NSLog(@"%d",a);        };        a = 100;        block();    }    return 0;}




其实上面的有__block和没有__block是很好理解的,结合前面的block实质的分析,block是一个结构体,而他的实例是通过调用该结构体的构造函数来创建的,在函数中如何改变外部传递的变量的值?只有将它的地址传递进来,所以查看c++源码可以发现__block确实传递的是a的地址,而没有__block的是直接传递a的值
如下:
带有__block的


不带有__block的:



4.block的循环引用问题
Block循环引用是值block代码块中引用了外部对象,而外部对象又同时引用了block,此时就会出现循环引用
例:
int main(int argc, const char * argv[]) {    @autoreleasepool {        Person *person = [[Person alloc] init];        person.name = @"张三";        [person setOperation:^{            NSLog(@"%@",person.name);//这里会造成循环引用        }];    }    return 0;}







从上图看出,block被拷贝到堆中,Person中的operation变量对block有强引用,而block对person有强引用,所以造成循环引用了
解决方法是:将block访问的外部对象使用__weak或者____unsafe_unretained
例:
int main(int argc, const char * argv[]) {    @autoreleasepool {        Person *person = [[Person alloc] init];        person.name = @"张三";        __weak typeof(person) weakPerson  = person;        [person setOperation:^{            NSLog(@"%@",weakPerson.name);//这里会造成循环引用        }];    }    return 0;}



第二种比较隐晦的循环引用
例:
- (instancetype)init{    if (self = [super init]) {        [self setOperation:^{           _name = @"sss";//这里也会造成循环引用,因为_name就是self->name        }];    }    return self;}


这种循环引用经常会被忽略
0 0
原创粉丝点击