一 block基本使用
二 block底层结构
三 block变量捕获
四 block的类型
五 block对象类型的自变量内存管理
六 _block讲解
七 block循环引用问题
block本质上也是一个OC对象,它内部也有个isa指针,block是封装了函数调用以及函数调用环境的OC对象,下面我们来看一下它的基本使用
一 block基本使用
它的基本使用我们用一段代码来演示
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
//匿名block
^{
NSLog(@"block this is");
}();
//无参数 无返回值的block
void (^block1)(void)= ^{
NSLog(@"无参数,无返回值的block");
};
block1();
//有参数无返回值的block
void (^block2)(int,int) = ^(int a, int b) {
NSLog(@"%d,%d",a,b);
};
block2(11,29);
//有参数有返回值的block
int (^block3)(int,int) = ^(int a, int b) {
return a + b;
};
NSLog(@"%d",block3(10,20));
}
output:
2019-02-13 15:45:37.091869+0800 test[9816:170053] block this is
2019-02-13 15:45:37.092465+0800 test[9816:170053] 无参数,无返回值的block
2019-02-13 15:45:37.092518+0800 test[9816:170053] 11,29
2019-02-13 15:45:37.092555+0800 test[9816:170053] 30
下图是block的基本定义结构图:
block是封装了函数调用以及函数调用环境的OC对象,首先它是的对象,是对象就可以调用方法,当作参数传递等等。一段代码逻辑可以当作参数传递给另一个函数,是不是很赞,但是它底层是怎么实现的呢,让我们一起来看一下
二 block底层结构
首先我们把下面一段示例代码编译成C++来看一下
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
//有参数无返回值的block
void (^block2)(int,int) = ^(int a, int b) {
NSLog(@"%d,%d",a,b);
};
block2(11,29);
}
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
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) { //c++构造函数
impl.isa = &_NSConcreteStackBlock; //block类型
impl.Flags = flags;
impl.FuncPtr = fp; //方法地址
Desc = desc; //描述,block大小等
}
};
// NSLog(@"%d,%d",a,b);
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_6936c3_mi_0,a,b);
}
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()
{
//定义一个block
void (*block2)(int,int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
//执行block内部代码
((void (*)(__block_impl *, int, int))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2, 11, 29);
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
最后我们精简一下就可以看出block的具体结构了:
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;
};
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
通过底层结构我们可以看出block里面有isa指针,有函数,由此可知它是一个对象,block里面的逻辑被封装成一个函数,函数地址放在了*FuncPtr
里面.我们也可以通过下面一段代码来验证我们所说的。
#include<stdio.h>
#import <Foundation/Foundation.h>
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;
};
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
int main()
{
//有参数无返回值的block
void (^block2)(int,int) = ^(int a, int b) {
NSLog(@"%d,%d",a,b);
};
struct __main_block_impl_0 *dblock = (__bridge struct __main_block_impl_0*)block2;
NSLog(@"%@",[block2 superclass]);
NSLog(@"%@",[[block2 superclass] superclass]);
NSLog(@"%@",[[[block2 superclass] superclass] superclass]);
}
结果如下:
我们可以看到block最终父类就是NSObject,下面就是block的底层结构图:
好了上面就是block一个底层的大致结构,下面我们更深入的来了解一下。
三 block变量捕获
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制 ,变量我们有:局部变量,全局变量,静态变量,非静态变量。不同的变量block的捕获机制是不一样的,我们先来看下面的一张总结表格
变量类型 | 是否捕获到block内部 | 访问方式 |
---|---|---|
局部变量 auto | 是 | 值传递 |
局部变量 static | 是 | 指针传递 |
全局变量 | 否 | 直接访问 |
下面我们对这三种情况的变量捕获分别讲解
3.1 局部变量自动变量类型的捕获
请看下面一段代码
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
int grade = 3; //auto int grade = 3;
void (^block2)(void) = ^{
NSLog(@"年级是%d",grade);
};
grade = 10;
block2();
}
output:
2019-02-17 11:29:13.737438+0800 test[61971:1312228] 年级是3
Program ended with exit code: 0
我们看一下转译后的C++
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int grade; //grade 变量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _grade, int flags=0) : grade(_grade) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int grade = __cself->grade; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_f4c6af_mi_0,grade);
}
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 grade = 3; //定义grade
void (*block2)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, grade)); //最后一个参数传入了grade变量
grade = 10;
((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
}
通过C++代码我们可以看出,block内部也有一个grade变量,在定义block时,直接把外部变量的值传递给block内部的变量,对于这种情况我们总结一下:
局部自动变量的捕获
1 什么时间捕获:定义block时捕获外部局部变量
2 捕获的是什么:捕获的是变量的值
3 一旦捕获,block内部变量跟外部的局部变量就没有关系了,无论外部的局部变量怎么变,都不会影响到block内部的变量,反之毅然。
OK 下面我们接着看局部静态变量。
3.2 局部静态变量的捕获
我们直接把上面的grade变成静态变量来看一下:
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
static int grade = 3;
void (^block2)(void) = ^{
NSLog(@"年级是%d",grade);
};
grade = 10;
block2();
}
2019-02-17 11:51:40.337578+0800 test[62530:1324223] 年级是10
输出结果是10,可以看到跟自动变量捕获的输出结果不一样,我们再来看一下转译后的C++代码:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *grade; //grade指针变量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_grade, int flags=0) : grade(_grade) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *grade = __cself->grade; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_9e8152_mi_0,(*grade));
}
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()
{
static int grade = 3;
void (*block2)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &grade));//此处传递的是grade的地址
grade = 10;
((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
}
通过C++代码我们可以看到对于静态的局部变量,block捕获的是grade的地址,下面我们来总结一下:
局部静态变量的捕获
1 什么时间捕获:定义block时捕获外部局部变量
2 捕获的是什么:捕获的是变量的地址
3 一旦捕获,block内部变量跟外部的局部变量指向的内存地址都是同一个,无论外部的局部变量怎么变都会影响到block内部的变量,反之毅然。
OK 下面我们接着看全局变量的捕获。
3.3 全局变量的捕获
我们还是上面的代码,我们把grade方法全局区域,下面我们来看下代码:
#include<stdio.h>
#import <Foundation/Foundation.h>
int grade = 3;
int main()
{
void (^block2)(void) = ^{
NSLog(@"年级是%d",grade);
};
grade = 10;
block2();
}
output:
2019-02-17 12:00:43.662399+0800 test[62766:1329510] 年级是10
Program ended with exit code: 0
我们看到输出结果是10,跟局部静态变量捕获输出结果一样,下面我们来看一下它的底层实现是怎样的,转成C++代码如下:
#pragma clang assume_nonnull end
int grade = 3; //定义在全局
struct __main_block_impl_0 { //也没有任何内部grade变量
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;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_b85f84_mi_0,grade);
}
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()
{
void (*block2)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); //什么都没传
grade = 10;
((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
}
通过C++代码我们可以看到对于全局变量block什么也没做,用的时候就是直接访问,下面我们来总结一下:
局部静态变量的捕获
1 什么时间捕获:什么时候都不捕获
2 捕获的是什么:什么也不捕获,用的时候就是直接访问
3 因为是全局变量,大家谁都可以修改访问,所以任何修改都会影响其他的使用者。
OK 这三种变量的捕获情况我们已经讲解完了,那么为什么会有这种差异呢,下面我们来解释一下。
3.4 三种变量捕获的差异原因?
#include<stdio.h>
#import <Foundation/Foundation.h>
int grade = 3;
void (^block)(void);
void blockTest(){
int age = 10;
static int sex = 1;
block = ^{
NSLog(@"年级是%d,年龄是:%d,性别是:%d",grade,age,sex);
};
}
int main()
{
blockTest();
grade = 3; //全局变量谁都可以访问
age = 20; //blockTest内部定义的,无法直接使用,blockTest调用完此变量就会销毁
sex = 0;//blockTest内部定义的,无法直接使用,blockTest调用完此变量不会销毁
block();
}
通过上面的代码,我们可以总结原因如下:
1 因为作用域的原因,全局变量什么地方都可以访问,block内部,函数内部都可以直接访问,所以block没必要去捕获它。
2 而对于局部变量,我们的block可以在很多地方调用,假如我在一个函数内部给它赋值并且这个block使用了局部变量,我在别处要想正常调用,我就需要把这个局部变量捕获到我内部,这时候谁调用我都不会有问题。
3 那为什么自动变量是捕获值,静态变量是捕获地址呢?那是因为自动变量和静态变量的生命周期不同,自动变量函数执行完就销毁了,而静态变量不会销毁。所以我不能捕获自动变量的地址,变量销毁了,我再访问它地址就会出错的。
OK上面就是block变量捕获的相关内容,下面我们接着讲,我们来看一看block的类型。
四 block的类型
block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型 ,具体类型如下:
序号 | 类型 | 同名类型 | 如何区分 |
---|---|---|---|
1 | NSGlobalBlock | _NSConcreteGlobalBlock | 没有访问auto变量 |
2 | NSStackBlock | _NSConcreteStackBlock | 访问了auto变量 |
3 | NSMallocBlock | _NSConcreteMallocBlock | NSStackBlock调用了copy |
那么这三种block有什么意义呢,为什么要分三种呢,其实跟变量分全局,局部一样,之所以分是哪种,也是它们的生命周期不同,它们的内存管理也不同,下面我们看一张这三种类型的block内存分布情况:
数据段
字符串常量:比如NSString *str = @"helloworld"
已初始化数据:已初始化的全局变量、静态变量等
未初始化数据:未初始化的全局变量、静态变量等
栈:函数调用开销,比如局部变量。
分配的内存空间地址越来越小
内存程序自动管理
堆:通过alloc、malloc、calloc等动态分配的空间,分配的内存空间地址越来越大
内存需要手动管理
下面我们来具体讲解这三种类型的block
4.1 NSGlobalBlock
没有访问auto变量的block就是NSGlobalBlock,我们代码来看一下
#include<stdio.h>
#import <Foundation/Foundation.h>
int grade = 3;
int main()
{
static int sex = 1;
void (^block)(void)= ^{
NSLog(@"年级是%d,性别是:%d",grade,sex);
};
NSLog(@"%@",[block class]);
}
2019-02-17 15:24:29.725967+0800 test[68901:1440931] __NSGlobalBlock__
Program ended with exit code: 0
根据输出结果我们可以看出,不管你访问的是全局变量还是静态局部变量,只要你访问的不是自动变量,它的类型就是NSGlobalBlock的,它的内存分布在数据段。
4.2 NSStackBlock
访问了auto变量 的block就是NSStackBlock类型,我们代码来看一下:
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
int age = 11;
void (^block)(void)= ^{
NSLog(@"年龄是%d",age);
};
NSLog(@"%@",[block class]);
}
这里要关掉ARC才能打印出它的真实类型,因为ARC会根据实际情况做一些block类型的转换而MRC则不会。
output:
2019-02-17 15:34:12.390039+0800 test[69157:1447001] __NSStackBlock__
Program ended with exit code: 0
NSStackBlock放在栈区,一旦block超出它的作用域,就会自动销毁,有时候就会出现一些问题,我下面举个🌰
#include<stdio.h>
#import <Foundation/Foundation.h>
void (^block)(void);
void blockTest() {
int age = 11;
block = ^{
NSLog(@"年龄是%d",age);
};
NSLog(@"%@",[block class]);
}
int main()
{
blockTest();
block();
}
2019-02-17 15:44:09.092300+0800 test[69453:1454830] __NSStackBlock__
2019-02-17 15:44:09.092890+0800 test[69453:1454830] 年龄是-272632344
Program ended with exit code: 0
由输出结果可以看出打印的age是乱码的,为什么呢?那是因为我们这个block是放在栈上的,一旦执行完blockTest(),那么这个block的一些访问的变量就会被释放,此时age就释放了,这时你再调用block,访问的age已经释放了,所以就给你个随机值了,为了解决这种类似问题,那就是下面我们要讲的这种block了。
4.3 NSMallocBlock
NSStackBlock调用了copy就会转换成NSMallocBlock,我们把4.2的示例代码稍微改一下,也是在MRC环境下运行
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
int age = 11;
void (^block)(void)= [^{
NSLog(@"年龄是%d",age);
} copy];
NSLog(@"%@",[block class]);
}
2019-02-17 15:37:09.265120+0800 test[69245:1449195] __NSMallocBlock__
Program ended with exit code: 0
好,4.2我们讲过NSStackBlock存在的问题,那我们看看NSMallocBlock能不能解决这个问题。
#include<stdio.h>
#import <Foundation/Foundation.h>
void (^block)(void);
void blockTest() {
int age = 11;
block = [^{
NSLog(@"年龄是%d",age);
} copy];
NSLog(@"%@",[block class]);
}
int main()
{
blockTest();
block();
}
2019-02-17 15:55:16.512882+0800 test[69739:1461301] __NSMallocBlock__
2019-02-17 15:55:16.513941+0800 test[69739:1461301] 年龄是11
Program ended with exit code: 0
我们看出年龄能正确输出,为什么呢?那是因为此时的block是放在堆上的,那么它的内存是由我们自己来管理的,只要我们不释放,它上面的内容就一直在,blockTest()函数执行完age并不会释放,它需要我们自己来释放。有这个问题我们也能看出NSStackBlock与NSMallocBlock的区别,就是内存管理方式与变量的生命长短有差异。
NSStackBlock调用copy就转换成NSMallocBlock那其他两个类型的调用copy是什么类型呢,下面我们来看一下关于三种block copy操作的一下细节
4.4 block copy细节
下面是一张总结表格,我们先来看一下:
Block的类型 | 副本源的配置存储域 | 复制效果 |
---|---|---|
NSStackBlock | 栈 | 从栈复制到堆 |
NSGlobalBlock | 数据区 | 什么也不做 |
NSMallocBlock | 堆 | 引用计数增加 |
下面是一段演示代码:
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
//NSGlobalBlock
void (^block)(void)= [^{
NSLog(@"BLOCK");
} copy];
NSLog(@"%@",[block class]);
NSLog(@"%lu",(unsigned long)[block retainCount]);
//NSGlobalBlock
int age = 11;
void (^block2)(void)= [^{
NSLog(@"年龄是%d",age);
} copy];
NSLog(@"%@",[block2 class]);
NSLog(@"%lu",(unsigned long)[block2 retainCount]);
//NSMallocBloc
int age3 = 11;
void (^block3)(void)= [[^{
NSLog(@"年龄是%d",age3);
} copy] copy];
NSLog(@"%@",[block3 class]);
NSLog(@"%lu",(unsigned long)[block3 retainCount]);
[block3 release];
NSLog(@"%lu",(unsigned long)[block3 retainCount]);
[block3 release];
NSLog(@"%lu",(unsigned long)[block3 retainCount]);
//往下调用就崩溃了
[block3 release];
NSLog(@"%lu",(unsigned long)[block3 retainCount]);
}
output:
2019-02-17 16:30:21.931456+0800 test[70715:1484032] __NSGlobalBlock__
2019-02-17 16:30:21.931868+0800 test[70715:1484032] 1
2019-02-17 16:30:21.932009+0800 test[70715:1484032] __NSMallocBlock__
2019-02-17 16:30:21.932049+0800 test[70715:1484032] 1
2019-02-17 16:30:21.932085+0800 test[70715:1484032] __NSMallocBlock__
2019-02-17 16:30:21.932121+0800 test[70715:1484032] 1
2019-02-17 16:30:21.932152+0800 test[70715:1484032] 1
2019-02-17 16:30:21.932180+0800 test[70715:1484032] 1
对于NSMallocBlock的copy我做一下说明:NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数,通过输出结果我们可以知道虽然NSMallocBlock每次的retainCount打印都是1,但是当我释放两次都没问题,第三次就会出错,说明它内部还是有内存管理机制的,之所打印的retainCount为1 ,可能block的retainCount做了特殊处理吧。
所有上面的copy操作都是在MRC情况下的,那么ARC环境下copy操作有哪些细节我们来看一下:
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况(根据上面copy情况表,下面情况都是针对与NSStackBlock的)
:
1 block作为函数返回值时
#include<stdio.h>
#import <Foundation/Foundation.h>
typedef void (^MyBlock)(void);
MyBlock myblock(){
int i = 10;
return ^{
NSLog(@"block:%d",i);
};
};
int main()
{
MyBlock block = myblock();
block();
NSLog(@"%@",[block class]);
}
2 将block赋值给__strong指针时
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
int i = 10;
__strong void (^myBlock)(void) = ^{
NSLog(@"block:%d",i);
};
NSLog(@"%@",[myBlock class]);
}
3 block作为Cocoa API中方法名含有usingBlock的方法参数时
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
NSArray *array = @[@1,@2,@5];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
}
4 block作为GCD API的方法参数时
#include<stdio.h>
#import <Foundation/Foundation.h>
int main()
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
}
ARC下block属性的建议写法 :
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
MRC下block属性的建议写法 :
@property (copy, nonatomic) void (^block)(void);
好了到此block的一些基本结构类型我们就讲完了,接下来我们看一下block一些特殊关键字,以及内存管理方面的内容
五 block对象类型的自变量内存管理
block对象类型的自变量内存管理就是block引用了局部变量,而这个局部变量是个对象,之前我们讲变量捕获讲的都是基本数据类型,并不是对象,那么如果变量是个对象block是如何管理这个对象内存的呢,我们来看一下。
当block内部访问了对象类型的auto变量时
1 如果block是在栈上,将不会对auto变量产生强引用
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property(assign,nonatomic) int age;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@end
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
{
Person *person = [[Person alloc] init];
person.age = 10;
NSLog(@"%@", [^{
NSLog(@"%d",person.age);
} class]);
}
NSLog(@"-------");
}
2019-02-17 18:47:15.194739+0800 test[74366:1566585] __NSStackBlock__
2019-02-17 18:47:15.195209+0800 test[74366:1566585] -[Person dealloc]
2019-02-17 18:47:29.249951+0800 test[74366:1566585] -------
只要MyBlock在栈空间,那么当它离开作用域,它与所引用的对象就会断开连接。
2 如果block被拷贝到堆上
- 会调用block内部的copy函数
- copy函数内部会调用
_Block_object_assign
函数 -
_Block_object_assign
函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained
)做出相应的操作,形成强引用(retain)或者弱引用
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
typedef void (^MyBlock)(void);
int main()
{
MyBlock myBlock;
{
__strong Person *person = [[Person alloc] init];
person.age = 10;
// Person *weakPerson = person;
myBlock = ^{
NSLog(@"%d",person.age);
};
}
NSLog(@"%@",[myBlock class]);
myBlock();
NSLog(@"-------");
}
2019-02-17 20:37:07.693841+0800 test[77005:1623211] __NSMallocBlock__
2019-02-17 20:37:07.694227+0800 test[77005:1623211] 10
2019-02-17 20:37:09.914947+0800 test[77005:1623211] -------
2019-02-17 20:37:09.915220+0800 test[77005:1623211] -[Person dealloc]
typedef void (*MyBlock)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
Person *person = __cself->person; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_76f5bc_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("age")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 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()
{
MyBlock myBlock;
{
__attribute__((objc_ownership(strong))) Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)person, sel_registerName("setAge:"), 10);
myBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, person, 570425344));
}
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_76f5bc_mi_1,((Class (*)(id, SEL))(void *)objc_msgSend)((id)myBlock, sel_registerName("class")));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_76f5bc_mi_2);
}
3 如果block从堆上移除
- 会调用block内部的dispose函数
- dispose函数内部会调用
_Block_object_dispose
函数 -
_Block_object_dispose
函数会自动释放引用的auto变量(release)
从上面截取的......
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};
关于block局部变量是对象的内存管理问题我们就讲完了,下面做两题练练。
问题: p 会在什么时候释放
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
People *p = [[People alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",p);
});
}
@end
答:p会在定时器启动3秒后执行完block里的内容后释放,为什么在三秒后,前面我们知道,对于GCD的block,会对对象进行强引用所以定时器不执行对象就不会释放。
问题: p 会在什么时候释放
#import "ViewController.h"
#import "People.h"
@interface ViewController ()
@property(strong,nonatomic) People *people;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
People *p = [[People alloc] init];
__weak People *weakPeople = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakPeople);
});
}
@end
答:p会立马释放 __weak是弱引用
问题: p 会在什么时候释放
#import "ViewController.h"
#import "People.h"
@interface ViewController ()
@property(strong,nonatomic) People *people;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
People *p = [[People alloc] init];
__weak People *weakPeople = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakPeople);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",p);
});
});
}
答:6秒后释放,整体里面强引用优先。
OK 到此block应用对象类型内存管理问题我们讲到这,我们接下来看下面的内容
六 _block讲解
如果想要在block内部修改auto变量值怎么办呢?那么这个_block就派上用场了。
__block可以用于解决block内部无法修改auto变量值的问题
(注意变量值这个概念,如果是修改指针对象里的内容就不叫修改变量值)
__block不能修饰全局变量、静态变量(static)
(全局变量任何地方都可以改,静态变量block里保存的是变量地址值也可以改,所以就没必要用__block修饰)
下面我们来看一下 __block基本使用
6.1 __block基本使用
#include<stdio.h>
#import <Foundation/Foundation.h>
typedef void (^MyBlock)(void);
int main()
{
MyBlock myBlock;
__block int grade = 10;
myBlock = ^{
grade = 20;
NSLog(@"%d",grade);
};
NSLog(@"%d",&grade); //grade的地址值,不是它包装对象的地址值
myBlock();
}
2019-02-18 09:31:35.247437+0800 test[99712:2302267] 20
使用起来很简单,下面我们来看一下它底层是如何实现的,我们把上面的代码转译成c++代码:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
#pragma clang assume_nonnull end
typedef void (*MyBlock)(void);
struct __Block_byref_grade_0 {
void *__isa;
__Block_byref_grade_0 *__forwarding;
int __flags;
int __size;
int grade;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_grade_0 *grade; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_grade_0 *_grade, int flags=0) : grade(_grade->__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_grade_0 *grade = __cself->grade; // bound by ref
(grade->__forwarding->grade) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_785a23_mi_0,(grade->__forwarding->grade));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->grade, (void*)src->grade, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->grade, 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()
{
MyBlock myBlock;
__attribute__((__blocks__(byref))) __Block_byref_grade_0 grade = {(void*)0,(__Block_byref_grade_0 *)&grade, 0, sizeof(__Block_byref_grade_0), 10};
myBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_grade_0 *)&grade, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
跟以前的局部自动变量捕获我们发现有下面的不一样的地方
struct __Block_byref_grade_0 {
void *__isa; //isa指针
__Block_byref_grade_0 *__forwarding; //前置指针,指向自己
int __flags;
int __size; //结构体大小
int grade;//grade的值
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_grade_0 *grade; // by ref
};
int main()
{
MyBlock myBlock;
//把gradle包装成一个对象
__Block_byref_grade_0 grade = {&grade, //传入了变量的地址
0,
sizeof(__Block_byref_grade_0), 10};
myBlock = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &grade, 570425344)); 把grade对象地址值传给block
myBlock->FuncPtr(myBlock);
}
//调用修改使用 grade对象的->__forwarding指针->grade变量
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_grade_0 *grade = __cself->grade; // bound by ref
(grade->__forwarding->grade) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_785a23_mi_0,(grade->__forwarding->grade));
}
所以:编译器会将__block变量包装成一个对象 __Block_byref_grade_0
把这个对象的地址给block,block通过包装的对象修改这个变量值,包装对象结构图如下:
既然是包装成对象那么就会有内存管理问题,下面我们来看一下
6.2 __block 内存管理
我们还是拿出上面c++代码的一部分来说明
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->grade, (void*)src->grade, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->grade, 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*);
}
当block在栈上时,并不会对__block变量产生强引用
-
当block被copy到堆时
会调用block内部的copy函数
copy函数内部会调用_Block_object_assign
函数
_Block_object_assign
函数会对__block变量形成强引用(retain)
如果有两个block在栈上,都引用了同一个__block变量,那么当它们拷贝到堆上时候,block 和__block都会拷贝一份到堆上,当然了__block是共用的它只copy一次,堆上的也都是强引用。
-
当block从堆中移除时
会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的__block变量(release)
释放的时候,当所有block都不引用__block,__block才会销毁。我们可以看出__block的实现内存管理跟block引用对象类型的的局部变量内存管理很像,看下面代码
#include<stdio.h>
#import <Foundation/Foundation.h>
typedef void (^MyBlock)(void);
int main()
{
MyBlock myBlock;
__block int grade = 10;
NSObject *obj = [[NSObject alloc] init];
myBlock = ^{
grade = 20;
NSLog(@"%d",grade);
NSLog(@"%@",obj);
};
myBlock();
}
它们唯一不同的就是__block修饰的变量是强引用,而obj 可以根据__strong,__weak,来决定是强引用还是弱引用,下面我们来总结下这两种变量的内存管理情况,
对象类型的auto变量、__block变量总结
- 当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 修饰的是基本类型的变量,那么当__block修饰的是对象类型的变量,内存管理又是怎么样的呢,我们来看一下
6.3 __block 对象类型内存管理
先看段代码:
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
typedef void (^MyBlock)(void);
int main()
{
MyBlock myBlock;
__block __strong Person *person = [[Person alloc] init];
__block __weak Person *weakPerson = person;
myBlock = ^{
NSLog(@"%@,%@",person,weakPerson);
};
myBlock();
}
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property(assign,nonatomic) int age;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@end
编译成的C++代码如下:
#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
typedef void (*MyBlock)(void);
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*);
Person *__strong person;
};
struct __Block_byref_weakPerson_1 {
void *__isa;
__Block_byref_weakPerson_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__weak weakPerson;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_person_0 *person; // by ref
__Block_byref_weakPerson_1 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, __Block_byref_weakPerson_1 *_weakPerson, int flags=0) : person(_person->__forwarding), weakPerson(_weakPerson->__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_person_0 *person = __cself->person; // bound by ref
__Block_byref_weakPerson_1 *weakPerson = __cself->weakPerson; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_802ae9_mi_0,(person->__forwarding->person),(weakPerson->__forwarding->weakPerson));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->weakPerson, 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()
{
MyBlock myBlock;
__attribute__((__blocks__(byref))) __attribute__((objc_ownership(strong))) __Block_byref_person_0 person = {(void*)0,(__Block_byref_person_0 *)&person, 33554432, sizeof(__Block_byref_person_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"))};
__attribute__((__blocks__(byref))) __attribute__((objc_ownership(weak))) __Block_byref_weakPerson_1 weakPerson = {(void*)0,(__Block_byref_weakPerson_1 *)&weakPerson, 33554432, sizeof(__Block_byref_weakPerson_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, (person.__forwarding->person)};
myBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_person_0 *)&person, (__Block_byref_weakPerson_1 *)&weakPerson, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
我们来精简一下
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*);
Person *__strong person;
};
struct __Block_byref_weakPerson_1 {
void *__isa;
__Block_byref_weakPerson_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__weak weakPerson;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_person_0 *person; // by ref
__Block_byref_weakPerson_1 *weakPerson; // by ref
};
我们可以看到__block 对于对象类型也会进行包装,但是包装的对象多了两个方法void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*);
,由此我们可以看到block引用的局部对象变量的内存是由__block的包装对象来管理的,根据上面的代码,我们整理出一个对象结构图如下:
那他们的内存管理是怎样的呢?
我们看下面两行代码
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
1.当__block变量在栈上时,不会对指向的对象产生强引用
2 .当__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对象类型内存管理的一些讲解,最后针对于__block其他一些细节我们简单讲一下
6.4 __block的__forwarding指针
上面6.1C++有段代码我们来看一下
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_grade_0 *grade = __cself->grade; // bound by ref
(grade->__forwarding->grade) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rn_r6_l2xln77j0bv69j2c_5rg00000gp_T_main_785a23_mi_0,(grade->__forwarding->grade));
}
我们在block内部修改变量的时候为什么要通过__forwarding指针呢,直接通过grade->grade = 20,不更简单么,之所以这样设计那就牵扯到block拷贝的问题了,我们看下面一张图:
当block在栈上的时候,__forwarding指向的是栈上的__block结构体,当block复制到堆上的时候__forwarding指向的是堆上的__block结构体这样就能保证当block拷贝到堆上时你能访问到堆上的变量
(比如访问堆上的grade变量,否则不这样干grade结构体怎么访问堆上的grade变量呢,一旦拷贝到堆上__forwarding指向就发生改变,这样才能保证你能访问到堆上的变量)
。OK到此block基本内容就讲的差不多了,最后我们再讲解下block的循环引用问题。
七 block循环引用问题
我们来看一下循环引用的例子:
#import <Foundation/Foundation.h>
typedef void (^MyBlock)(void);
@interface Person : NSObject
@property(copy,nonatomic) MyBlock block;
@property(copy,nonatomic) NSString *name;
@end
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@end
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *person = [[Person alloc] init];
person.name = @"张三";
person.block = ^{
NSLog(@"%@",person.name);
};
person.block();
}
2019-02-18 12:54:36.587115+0800 test[5464:2439742] 张三
Program ended with exit code: 0
我们可以看到代码执行完,我们的person对象也没有释放,我们的person里有一个成员变量是block,这个block又引用了person的一个成员变量,你引用我我引用你,就形成了一个闭环的引用,释放的时候你等我释放,我等你释放,结果谁也无法释放,这就造成了循环引用问题,这个问题在平时开发中还是很常见的,怎么解决这个问题呢,我们接下来往下看。
ARC下解决循环引用问题
1 使用__weak
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *person = [[Person alloc] init];
person.name = @"张三";
__weak Person *weakPerson = person;
person.block = ^{
NSLog(@"%@",weakPerson.name);
};
person.block();
}
2019-02-18 13:02:20.494467+0800 test[5663:2445602] 张三
2019-02-18 13:02:20.494898+0800 test[5663:2445602] -[Person dealloc]
Program ended with exit code: 0
2 用__block解决(必须要调用block)
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *person = [[Person alloc] init];
person.name = @"张三";
__block Person *weakPerson = person;
person.block = ^{
NSLog(@"%@",weakPerson.name);
weakPerson = nil;
};
person.block();
}
这种方案要执行block才不会有循环引用问题,如果你不执行,就会存在循环引用问题,不太建议使用。
MRC下解决循环引用问题
1 用__unsafe_unretained解决
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (void)dealloc
{
[super dealloc];
NSLog(@"%s",__func__);
}
@end
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *person = [[Person alloc] init];
person.name = @"张三";
__unsafe_unretained Person *weakPerson = person;
person.block = ^{
NSLog(@"%@",weakPerson.name);
};
person.block();
[person release];//不加__unsafe_unretained即使调用release person也不会释放
}
MRC不支持,要使用弱引用指针只能用__unsafe_unretained,不会产生强引用但是不安全。
2 使用__block
#include<stdio.h>
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *person = [[Person alloc] init];
person.name = @"张三";
__block Person *weakPerson = person;
person.block = ^{
NSLog(@"%@",weakPerson.name);
};
person.block();
[person release];
}
上面6.3讲过__block调用_Block_object_assign
不会产生强引用。
这几个常用的还是使用__weak来解决循环引用问题,主要目的就是打断循环闭环,无论破坏哪一条引用都可以。
OK 到此block相关的内容我们就讲完了。