对象类型的auto变量

例子一

首先看一个简单的例子

定义一个类 YZPerson,里面只有一个dealloc方法

@interface YZPerson : NSObject

@property (nonatomic ,assign) int age;

@end

@implementation YZPerson

- (void)dealloc

{

NSLog(@"%s",__func__);

}

@end

如下代码使用

int main(int argc, const char * argv[]) {

@autoreleasepool {

{

YZPerson *person = [[YZPerson alloc]init];

person.age = 10;

}

NSLog(@"-----");

}

return 0;

}

想必大家都能知道会输出什么,没错,就是person先销毁,然后打印----- 因为person是在大括号内,当大括号执行完之后,person 就销毁了。

iOS-block[1376:15527] -[YZPerson dealloc]

iOS-block[1376:15527] -----

例子二

上面的例子,是不是挺简单,那下面这个呢,

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZBlock block;

{

YZPerson *person = [[YZPerson alloc]init];

person.age = 10;

block = ^{

NSLog(@"---------%d", person.age);

};

NSLog(@"block.class = %@",[block class]);

}

NSLog(@"block销毁");

}

return 0;

}

如下结果,输出可知当 block为__NSMallocBlock__类型时候,block可以保住person的命的,因为person离开大括号之后没有销毁,当block销毁,person才销毁

iOS-block[3186:35811] block.class = __NSMallocBlock__

iOS-block[3186:35811] block销毁

iOS-block[3186:35811] -[YZPerson dealloc]

一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431, 分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。

分析

终端执行这行指令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m把main.m生成main.cpp

可以 看到如下代码

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

YZPerson *person;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *_person, int flags=0) : person(_person) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

很明显就是这个block里面包含 YZPerson *person。

MRC下 block引用实例对象

上面的例子,是不是挺简单,那如果是MRC下呢

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZBlock block;

{

YZPerson *person = [[YZPerson alloc]init];

person.age = 10;

block = ^{

NSLog(@"---------%d", person.age);

};

NSLog(@"block.class = %@",[block class]);

// MRC下,需要手动释放

[person release];

}

NSLog(@"block销毁");

// MRC下,需要手动释放

[block release];

}

return 0;

}

输出结果为

iOS-block[3114:34894] block.class = __NSStackBlock__

iOS-block[3114:34894] -[YZPerson dealloc]

iOS-block[3114:34894] block销毁

和上面的对比,区别就是,还没有执行NSLog(@"block销毁");的时候,[YZPerson dealloc]已经执行了。也就是说,person 离开大括号,就销毁了。

输出可知当 block为__NSStackBlock__类型时候,block不可以保住person的命的

MRC下 [block copy]引用实例对象

在MRC下,对block执行了copy操作

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZBlock block;

{

YZPerson *person = [[YZPerson alloc]init];

person.age = 10;

block = [^{

NSLog(@"---------%d", person.age);

} copy];

NSLog(@"block.class = %@",[block class]);

// MRC下,需要手动释放

[person release];

}

NSLog(@"block销毁");

[block release];

}

return 0;

输出结果为,可知当 block为__NSMallocBlock__类型时候,block是可以保住person的命的

iOS-block[3056:34126] block.class = __NSMallocBlock__

iOS-block[3056:34126] block销毁

iOS-block[3056:34126] -[YZPerson dealloc]

__weak修饰

如下代码

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZBlock block;

{

YZPerson *person = [[YZPerson alloc]init];

person.age = 10;

__weak YZPerson *weakPerson = person;

block = ^{

NSLog(@"---------%d", weakPerson.age);

};

NSLog(@"block.class = %@",[block class]);

}

NSLog(@"block销毁");

}

return 0;

}

输出为

iOS-block[3687:42147] block.class = __NSMallocBlock__

iOS-block[3687:42147] -[YZPerson dealloc]

iOS-block[3687:42147] block销毁

生成cpp文件

注意:

在使用clang转换OC为C++代码时,可能会遇到以下问题

cannot create __weak reference in file using manual reference

解决方案:支持ARC、指定运行时系统版本,比如

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

生成之后,可以看到,如下代码,MRC情况下,生成的代码明显多了,这是因为ARC自动进行了copy操作

//copy 函数

void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);

//dispose函数

void (*dispose)(struct __main_block_impl_0*);

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

//weak修饰

YZPerson *__weak weakPerson;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

static struct __main_block_desc_0 {

size_t reserved;

size_t Block_size;

//copy 函数

void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);

//dispose函数

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

};

//copy函数内部会调用_Block_object_assign函数

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {

//asssgin会对对象进行强引用或者弱引用

_Block_object_assign((void*)&dst->person,

(void*)src->person,

3/*BLOCK_FIELD_IS_OBJECT*/);

}

//dispose函数内部会调用_Block_object_dispose函数

static void __main_block_dispose_0(struct __main_block_impl_0*src) {

_Block_object_dispose((void*)src->person,

3/*BLOCK_FIELD_IS_OBJECT*/);

}

小结

无论是MAC还是ARC

当block为__NSStackBlock__类型时候,是在栈空间,无论对外面使用的是strong 还是weak 都不会对外面的对象进行强引用

当block为__NSMallocBlock__类型时候,是在堆空间,block是内部的_Block_object_assign函数会根据strong或者 weak对外界的对象进行强引用或者弱引用。

其实也很好理解,因为block本身就在栈上,自己都随时可能消失,怎么能保住别人的命呢?

当block内部访问了对象类型的auto变量时

如果block是在栈上,将不会对auto变量产生强引用

如果block被拷贝到堆上

会调用block内部的copy函数

copy函数内部会调用_Block_object_assign函数

_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

如果block从堆上移除

会调用block内部的dispose函数

dispose函数内部会调用_Block_object_dispose函数

_Block_object_dispose函数会自动释放引用的auto变量(release)

函数

调用时机

copy函数

栈上的Block复制到堆上

dispose函数

堆上的block被废弃时

__block

先从一个简单的例子说起,请看下面的代码

// 定义block

typedef void (^YZBlock)(void);

int age = 10;

YZBlock block = ^{

NSLog(@"age = %d", age);

};

block();

代码很简单,运行之后,输出

age = 10

上面的例子在block中访问外部局部变量,那么问题来了,如果想在block内修改外部局部的值,怎么做呢?

修改局部变量的三种方法

写成全局变量

我们把a定义为全局变量,那么在哪里都可以访问,

// 定义block

typedef void (^YZBlock)(void);

int age = 10;

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZBlock block = ^{

age = 20;

NSLog(@"block内部修改之后age = %d", age);

};

block();

NSLog(@"block调用完 age = %d", age);

}

return 0;

}

这个很简单,输出结果为

block内部修改之后age = 20

block调用完 age = 20

对于输出就结果也没什么问题,因为全局变量,是所有地方都可访问的,在block内部可以直接操作age的内存地址的。调用完block之后,全局变量age指向的地址的值已经被更改为20,所以是上面的打印结果

static修改局部变量

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

static int age = 10;

YZBlock block = ^{

age = 20;

NSLog(@"block内部修改之后age = %d", age);

};

block();

NSLog(@"block调用完 age = %d", age);

}

return 0;

}

上面的代码输出结果为

block内部修改之后age = 20

block调用完 age = 20

终端执行这行指令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m把main.m生成main.cpp

可以 看到如下代码

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

int *age;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age, int flags=0) : age(_age) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

int *age = __cself->age; // bound by copy

(*age) = 20;

NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_5dbaa1_mi_0, (*age));

}

可以看出,当局部变量用static修饰之后,这个block内部会有个成员是int *age,也就是说把age的地址捕获了。这样的话,当然在block内部可以修改局部变量age了。

以上两种方法,虽然可以达到在block内部修改局部变量的目的,但是,这样做,会导致内存无法释放。无论是全局变量,还是用static修饰,都无法及时销毁,会一直存在内存中。很多时候,我们只是需要临时用一下,当不用的时候,能销毁掉,那么第三种,也就是今天的主角 __block隆重登场

__block来修饰

代码如下

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

__block int age = 10;

YZBlock block = ^{

age = 20;

NSLog(@"block内部修改之后age = %d",age);

};

block();

NSLog(@"block调用完 age = %d",age);

}

return 0;

}

输出结果和上面两种一样

block内部修改之后age = 20

block调用完 age = 20

__block分析

终端执行这行指令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m把main.m生成main.cpp

首先能发现 多了__Block_byref_age_0结构体

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

// 这里多了__Block_byref_age_0类型的结构体

__Block_byref_age_0 *age; // by ref

// fp是函数地址 desc是描述信息 __Block_byref_age_0 类型的结构体 *_age flags标记

__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; //fp是函数地址

Desc = desc;

}

};

再仔细看结构体__Block_byref_age_0,可以发现第一个成员变量是isa指针,第二个是指向自身的指针__forwarding

// 结构体 __Block_byref_age_0

struct __Block_byref_age_0 {

void *__isa; //isa指针

__Block_byref_age_0 *__forwarding; // 指向自身的指针

int __flags;

int __size;

int age; //使用值

};

查看main函数里面的代码

// 这是原始的代码 __Block_byref_age_0

__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {

(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

// 这是原始的 block代码

YZBlock block = ((void (*)())&__main_block_impl_0(

(void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

代码太长,简化一下,去掉一些强转的代码,结果如下

// 这是原始的代码 __Block_byref_age_0

__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

//这是简化之后的代码 __Block_byref_age_0

__Block_byref_age_0 age = {

0, //赋值给 __isa

(__Block_byref_age_0 *)&age,//赋值给 __forwarding,也就是自身的指针

0, // 赋值给__flags

sizeof(__Block_byref_age_0),//赋值给 __size

10 // age 使用值

};

// 这是原始的 block代码

YZBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

// 这是简化之后的 block代码

YZBlock block = (&__main_block_impl_0(

__main_block_func_0,

&__main_block_desc_0_DATA,

&age,

570425344));

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

//简化为

block->FuncPtr(block);

其中__Block_byref_age_0结构体中的第二个(__Block_byref_age_0 *)&age赋值给上面代码结构体__Block_byref_age_0中的第二个__Block_byref_age_0 *__forwarding,所以__forwarding 里面存放的是指向自身的指针

//这是简化之后的代码 __Block_byref_age_0

__Block_byref_age_0 age = {

0, //赋值给 __isa

(__Block_byref_age_0 *)&age,//赋值给 __forwarding,也就是自身的指针

0, // 赋值给__flags

sizeof(__Block_byref_age_0),//赋值给 __size

10 // age 使用值

};

结构体__Block_byref_age_0中代码如下,第二个__forwarding存放指向自身的指针,第五个age里面存放局部变量

// 结构体 __Block_byref_age_0

struct __Block_byref_age_0 {

void *__isa; //isa指针

__Block_byref_age_0 *__forwarding; // 指向自身的指针

int __flags;

int __size;

int age; //使用值

};

调用的时候,先通过__forwarding找到指针,然后去取出age值。

(age->__forwarding->age));

小结

__block可以用于解决block内部无法修改auto变量值的问题

__block不能修饰全局变量、静态变量(static)

编译器会将__block变量包装成一个对象

调用的是,从__Block_byref_age_0的指针找到 age所在的内存,然后修改值

内存管理问题

bloc访问OC对象

代码如下

当block内部访问外面的OC对象的时候

eg:

// 定义block

typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {

@autoreleasepool {

NSObject *obj = [[NSObject alloc]init];

YZBlock block = ^{

NSLog(@"%p",obj);

};

block();

}

return 0;

}

在终端使用clang转换OC为C++代码

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

因为是在ARC下,所以会copy,栈上拷贝到堆上,结构体__main_block_desc_0中有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*);

}

copy会调用 __main_block_copy_0

static void __main_block_copy_0(struct __main_block_impl_0*dst,

struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj,

(void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

其内部的_Block_object_assign会根据代码中的修饰符 strong或者weak而对其进行强引用或者弱引用。

查看__main_block_impl_0

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

//strong 强引用

NSObject *__strong obj;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _obj, int flags=0) : obj(_obj) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

可以看上修饰符是strong,所以,调用_Block_object_assign时候,会对其进行强引用。

由前面可知

当block在栈上时,并不会对__block变量产生强引用

当block被copy到堆时

会调用block内部的copy函数

copy函数内部会调用_Block_object_assign函数

_Block_object_assign函数会对__block变量形成强引用(retain)

当block从堆中移除时

会调用block内部的dispose函数

dispose函数内部会调用_Block_object_dispose函数

_Block_object_dispose函数会自动释放引用的__block变量(release)

拷贝

拷贝的时候,

会调用block内部的copy函数

copy函数内部会调用_Block_object_assign函数

_Block_object_assign函数会对__block变量形成强引用(retain)

中我们知道,如下代码

__block int age = 10;

YZBlock block = ^{

age = 20;

NSLog(@"block内部修改之后age = %d",age);

};

局部变量age是在栈上的,在block内部引用age,但是当block从栈上拷贝到堆上的时候,怎么能保证下次block访问age的时候,能访问到呢?因为我们知道栈上的局部变量,随时会销毁的。

假设现在有两个栈上的block,分别是block0和block1,同时引用了了栈上的__block变量。现在对block0进行copy操作,我们知道,栈上的block进行copy,就会复制到堆上,也就是说block0会复制到堆上,因为block0持有__block变量,所以也会把这个__block变量复制到堆上,同时堆上的block0对堆上的__block变量是强引用,这样能达到block0随时能访问__block变量。

还是上面的例子,刚才block0拷贝到堆上了,现在如果block1也拷贝到堆上,因为刚才变量已经拷贝到堆上,就不需要再次拷贝,只需要把堆上的block1也强引用堆上的变量就可以了。

释放

当释放的时候

会调用block内部的dispose函数

dispose函数内部会调用_Block_object_dispose函数

_Block_object_dispose函数会自动释放引用的__block变量(release)

上面的代码中,如果在堆上只有一个block引用__block变量,当block销毁时候,直接销毁堆上的__block变量,但是如果有两个block引用__block变量,就需要当两个block都废弃的时候,才会废弃__block变量。

其实,说到底,就是谁使用,谁负责

对象类型的auto变量、__block变量

把前面的都放在一起整理一下,有 auto 变量 num , __block变量int, obj 和weakObj2如下

__block int age = 10;

int num = 8;

NSObject *obj = [[NSObject alloc]init];

NSObject *obj2 = [[NSObject alloc]init];

__weak NSObject *weakObj2 = obj2;

YZBlock block = ^{

NSLog(@"age = %d",age);

NSLog(@"num = %d",num);

NSLog(@"obj = %p",obj);

NSLog(@"weakObj2 = %p",weakObj2);

NSLog(@"block内部修改之后age = %d",age);

};

block();

执行终端指令

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

生成代码如下所示

被__block修饰的对象类型

当__block变量在栈上时,不会对指向的对象产生强引用

当__block变量被copy到堆时

会调用__block变量内部的copy函数

copy函数内部会调用_Block_object_assign函数

_Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)

如果__block变量从堆上移除

会调用__block变量内部的dispose函数

dispose函数内部会调用_Block_object_dispose函数

_Block_object_dispose函数会自动释放指向的对象(release)

__block的__forwarding指针

//结构体__Block_byref_obj_0中有__forwarding

struct __Block_byref_obj_0 {

void *__isa;

__Block_byref_obj_0 *__forwarding;

int __flags;

int __size;

void (*__Block_byref_id_object_copy)(void*, void*);

void (*__Block_byref_id_object_dispose)(void*);

NSObject *__strong obj;

};

// 访问的时候

age->__forwarding->age

为啥什么不直接用age,而是age->__forwarding->age呢?

这是因为,如果__block变量在栈上,就可以直接访问,但是如果已经拷贝到了堆上,访问的时候,还去访问栈上的,就会出问题,所以,先根据__forwarding找到堆上的地址,然后再取值

总结

当block在栈上时,对它们都不会产生强引用

当block拷贝到堆上时,都会通过copy函数来处理它们

__block变量(假设变量名叫做a)

_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量(假设变量名叫做p)

_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

当block从堆上移除时,都会通过dispose函数来释放它们

__block变量(假设变量名叫做a)

_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量(假设变量名叫做p)

_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

循环引用问题

继续探索一下block的循环引用问题。

看如下代码,有个Person类,里面两个属性,分别是block和age

#import

typedef void (^YZBlock) (void);

@interface YZPerson : NSObject

@property (copy, nonatomic) YZBlock block;

@property (assign, nonatomic) int age;

@end

#import "YZPerson.h"

@implementation YZPerson

- (void)dealloc

{

NSLog(@"%s", __func__);

}

@end

main.m中如下代码

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZPerson *person = [[YZPerson alloc] init];

person.age = 10;

person.block = ^{

NSLog(@"person.age--- %d",person.age);

};

NSLog(@"--------");

}

return 0;

}

输出只有

iOS-block[38362:358749] ——–

也就是说程序结束,person都没有释放,造成了内存泄漏。

循环引用原因

下面这行代码,是有个person指针,指向了YZPerson对象

YZPerson *person = [[YZPerson alloc] init];

执行完

person.block = ^{

NSLog(@"person.age--- %d",person.age);

};

之后,block内部有个强指针指向person,下面代码生成cpp文件

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

//强指针指向person

YZPerson *__strong person;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__strong _person, int flags=0) : person(_person) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

而block是person的属性

@property (copy, nonatomic) YZBlock block;

当程序退出的时候,局部变量person销毁,但是由于MJPerson和block直接,互相强引用,谁都释放不了。

__weak解决循环引用

为了解决上面的问题,只需要用__weak来修饰,即可

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZPerson *person = [[YZPerson alloc] init];

person.age = 10;

__weak YZPerson *weakPerson = person;

person.block = ^{

NSLog(@"person.age--- %d",weakPerson.age);

};

NSLog(@"--------");

}

return 0;

}

编译完成之后是

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

// block内部对weakPerson是弱引用

YZPerson *__weak weakPerson;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

当局部变量消失时候,对于YZPseson来说,只有一个若指针指向它,那它就销毁,然后block也销毁。

__unsafe_unretained解决循环引用

除了上面的__weak之后,也可以用__unsafe_unretained来解决循环引用

int main(int argc, const char * argv[]) {

@autoreleasepool {

YZPerson *person = [[YZPerson alloc] init];

person.age = 10;

__unsafe_unretained YZPerson *weakPerson = person;

person.block = ^{

NSLog(@"person.age--- %d",weakPerson.age);

};

NSLog(@"--------");

}

return 0;

}

对于的cpp文件为

struct __main_block_impl_0 {

struct __block_impl impl;

struct __main_block_desc_0* Desc;

YZPerson *__unsafe_unretained weakPerson;

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

虽然__unsafe_unretained可以解决循环引用,但是最好不要用,因为

__weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil

__unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变

__block解决循环引用

eg:

int main(int argc, const char * argv[]) {

@autoreleasepool {

__block YZPerson *person = [[YZPerson alloc] init];

person.age = 10;

person.block = ^{

NSLog(@"person.age--- %d",person.age);

//这一句不能少

person = nil;

};

// 必须调用一次

person.block();

NSLog(@"--------");

}

return 0;

}

上面的代码中,也是可以解决循环引用的。但是需要注意的是,person.block();必须调用一次,为了执行person = nil;.

对应的结果如下

下面的代码,block会对__block产生强引用

__block YZPerson *person = [[YZPerson alloc] init];

person.block = ^{

NSLog(@"person.age--- %d",person.age);

//这一句不能少

person = nil;

};

person对象本身就对block是强引用

@property (copy, nonatomic) YZBlock block;

__block对person产生强引用

struct __Block_byref_person_0 {

void *__isa;

__Block_byref_person_0 *__forwarding;

int __flags;

int __size;

void (*__Block_byref_id_object_copy)(void*, void*);

void (*__Block_byref_id_object_dispose)(void*);

//`__block`对person产生强引用

YZPerson *__strong person;

};

所以他们的引用关系如图

当执行完person = nil时候,__block解除对person的引用,进而,全都解除释放了。

但是必须调用person = nil才可以,否则,不能解除循环引用

小结

通过前面的分析,我们知道,ARC下,上面三种方式对比,最好的是__weak

MRC下注意点

如果再MRC下,因为不支持弱指针__weak,所以,只能是__unsafe_unretained或者__block来解决循环引用

结束

回到最开始的问题

block的原理是怎样的?本质是什么?

__block的作用是什么?有什么使用注意点?

block的属性修饰词为什么是copy?使用block有哪些使用注意?

block一旦没有进行copy操作,就不会在堆上

block在修改NSMutableArray,需不需要添加__block?

现在是不是心中有了自己的答案呢?

另外,如果你想一起进阶,不妨添加一下交流群1012951431,选择加入一起交流,一起学习。期待你的加入!

block的理解 ios_深入理解iOS的block (下)相关推荐

  1. 深入理解HTTPS及在iOS系统中适配HTTPS类型网络请求(上)

    2019独角兽企业重金招聘Python工程师标准>>> 深入理解HTTPS及在iOS系统中适配HTTPS类型网络请求 一.引言 本篇博客主要讨论如何在客户端与服务端之间进行HTTPS ...

  2. iOS之Block总结以及内存管理

    block定义 struct Block_descriptor {unsigned long int reserved;unsigned long int size;void (*copy)(void ...

  3. 有趣 IOS 开展 - block 使用具体解释

    Block 它是iOS于4.0新的程序语法之后,于iOS SDK 4.0之后,block应用几乎无处不在. 在其他语言中也有类似的概念,称为闭包(closure),实例object C兄弟swift ...

  4. IOS之block,一点小心得

    作为一个iOS开发程序员,没用过block是不可能的.这次我探讨的是block原理,但是有些更深层次的东西,我也不是很清楚,以后随着更加了解block将会慢慢完善. 第一个问题,什么是block? 我 ...

  5. 卷积在计算机中实现+pool作用+数据预处理目的+特征归一化+理解BN+感受野理解与计算+梯度回传+NMS/soft NMS

    一.卷积在计算机中实现 1.卷积 将其存入内存当中再操作(按照"行先序"): 这样就造成混乱. 故需要im2col操作,将特征图转换成庞大的矩阵来进行卷积计算,利用矩阵加速来实现, ...

  6. iOS开发 -------- Block技术中的weak - strong

    一 Block是什么? 我们使用^运算符来声明一个Block变量,而且在声明完一个Block变量后要像声明普通变量一样,后面要加; 声明Block变量 int (^block)(int) = NULL ...

  7. ios 添加block 类别_ios之Block的详细使用和具体说明

    image.png iOS代码块Block 一:概述 闭包 = 一个函数「或指向函数的指针」+ 该函数执行的外部的上下文变量「也就是自由变量」:Block 是 Objective-C 对于闭包的实现. ...

  8. java的接口怎么理解_如何理解Java 中的接口

    一.接口(Interface) 在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法.接口并不是类,编写接口的方 ...

  9. ios 添加block 类别_iOS 关于Block代码块的详解

    概述 block 上图就是一个block简单使用,它包括了block的声明.赋值实现.调用 三个部分,其中,实现部分可以看作是一种匿名函数:跟函数一样,block也是需要调用才能执行内部代码的:赋值的 ...

最新文章

  1. Tushare的安装
  2. UE研究方法—ethnography(人种志)
  3. CTF常用python库PwnTools的使用学习
  4. ClickHouse表引擎之Integration系列
  5. PyTorch多GPU并行训练方法及问题整理
  6. 618物流压力怎么扛?
  7. Vijos P1097 合并果子【哈夫曼树+优先队列】
  8. win10计算机磁盘图标,win10系统本地磁盘图标显示异常如何恢复
  9. adobe flash java_Adobe 发布最后的 Flash 更新:官方强烈建议立即卸载
  10. MSN 与六度分隔理论
  11. Linux CentOS 7 下载安装
  12. word转PDF图片消失问题
  13. 自动化 计算机方向非核心期刊,2015年自动化技术与计算机技术类核心期刊表
  14. P2P流媒体技术方案
  15. java课程任务_修真院java后端工程师学习课程--任务1(day three)
  16. 「RISC-V Arch」RISC-V 规范结构
  17. linux 内核 面试,LINUX内核经典面试题
  18. 计算机网络之TCP三次握手
  19. 织梦内容管理系统模板标签代码参考
  20. python 定时器 队列(5)

热门文章

  1. 硬件创业者们,如何避免掉到供应链的大坑里爬不出来
  2. HDU - 2609 - How many
  3. Zend Framework学习之常用校验器
  4. 我的ssis和ssas报错记录
  5. Bailian2815 城堡问题【DFS】
  6. 百练(十三~十六)题解
  7. Bailian2808 校门外的树【基础】
  8. HDU1210 Eddy's 洗牌问题【递推函数+模拟】
  9. CCF201312--模拟练习试题参考答案(Java)
  10. I00032 约瑟夫环(Joseph problem)