ios Block
1:捕获
2:block类型
2.1:问题 :mrc环境下 下面讲的都是mrc环境下,会真实很多
2.2:在arc下,block 自动加上copy的情况---:返回block。
2.3:在arc下,block 自动加上copy的情况---:强指针__block;
2.4:arc环境下:方法名中有usingBlock的方法参数时,也会进行copy操作。
2.5:在arc下,block 自动加上copy的情况---:block作为GCD的方法参数。
3:对象类型的auto变量
3.1:假设在mrc环境下
3.2:arc:对上面做一个原因解释
4:如何修改block内部的变量:__block、static、全局、指针
4.1:__block的内存管理
4.2:__forwarding指针原理
4.3:问题:blok内部会对下面两个对象有什么不同么
4.4:被__block修饰的对象类型:arc环境下
5:循环引用
下面以一个简单的例子开始讲起
int main(int argc, char * argv[]) {
@autoreleasepool {
int age = 10;
void (^ block)(void) = ^{
// age的值捕获进来,(capture)
NSLog(@"block:%d",age);
};
age = 20;
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
通过 (这里解释一下,编译如果xcode路径出问题可能导致编译失败,结果办法点击)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
把main.m -> main.cpp文件。
查看上面block的源码
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
void (* block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
age = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
整理一下就是
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10; // 自动变量auto,离开作用域便自动销毁 c语言特性
// 定义block变量
void (* block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
// 上面相当于是
void (* block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA
, age);
age = 20;
// 执行block内部的代码
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
// 上面相当于是:
block->FuncPtr(block);
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
__main_block_impl_0这个函数。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
// 构造函数(类似oc的init方法),返回结构体对象 这个也就是block指向的 c++的特性 传进来的_age会自动赋值给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;
}
};
// 封装了block执行逻辑的函数:block里面的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy 取出block里面的age
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_ff1ca7_mi_0,age);
}
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)};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
1:捕获
捕获:block内部会专门新增一个成员来外面变量的值,这个操作称之为捕获。
auto只存在于局部变量里,不能存在于全局变量中,因为它出了作用域就会被销毁。值传递,相当于把10传递进去。(auto是自动销毁的,所以必须要传值。)
static,址传递。看下面
int main(int argc, char * argv[]) {
@autoreleasepool {
int age = 10;
static int height = 10;
void (^ block)(void) = ^{
NSLog(@"block:%d height:%d",age,height);
};
age = 20;
height = 20;
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
所以上面的age捕获的是值,所以block内部的age跟外面的int age并不是一个,block里面的age是重新创建的,只是这里是把int age的值赋值给了block里面的age。只是因为这种情况下age无法在block里面进行赋值操作,所以age是不是改变的就不重要了但是也要注意,这种在block里面操作取值,长时间的操作,会有不准确性。(这种情况是局部变量age情况下)
上面的static height 跟block里面的*height是一个,因为__main_block_impl_0()这个函数传进去的是height指针,所以,这里block里面的*height修改了,同样外面的值也就修改了。
如果是全局变量
int age = 10;
static int height = 10;
int main(int argc, char * argv[]) {
@autoreleasepool {
void (^ block)(void) = ^{
NSLog(@"block:%d height:%d",age,height);
};
age = 20;
height = 20;
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
这个打印出来
2018-08-16 22:44:17.640 newxc[16609:5312874] block:20 height:20
源码
因为是全局变量,所以不论在哪里都能够访问,所以没有必要捕获。
(这种情况下,block内部的height和age,就是拿的全局变量的指针进行操作的,这本就是一个数据进行的操作。)
局部变量需要捕获,是因为局部变量的作用域只仅限在当前函数作用域,如果block函数和调用不在一个函数作用域,那么就会跨函数访问局部变量,所以需要捕获。
总结如下。
问:如果block内部调用self,会捕获么
#import "Person.h"
@implementation Person
- (void)test
{
void (^ block)(void) = ^{
NSLog(@"self:%p",self);
};
block();
}
@end
答案会捕获。
因为test在c语言中的写法是
参数就是局部变量,所以会被捕获。
person的test的c语言函数就是下面这样,Person * self, SEL _cmd这两个参数是默认必传的参数。
static void _I_Person_test(Person * self, SEL _cmd) {
void (* block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
// 看这里还有load方法
static void _C_Person_load(Class self, SEL _cmd) {
}
看一下上面的源码
// 看这里,里面有一个Person *self 参数里的self就会自动赋值给*self。
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
Person *self;
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 这里是block内部函数,在访问self。
static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
Person *self = __cself->self; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_Person_4d5a32_mi_0,self);
}
static void __Person__test_block_copy_0(struct __Person__test_block_impl_0*dst, struct __Person__test_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __Person__test_block_dispose_0(struct __Person__test_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __Person__test_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __Person__test_block_impl_0*, struct __Person__test_block_impl_0*);
void (*dispose)(struct __Person__test_block_impl_0*);
} __Person__test_block_desc_0_DATA = { 0, sizeof(struct __Person__test_block_impl_0), __Person__test_block_copy_0, __Person__test_block_dispose_0};
// 这里是test函数 __Person__test_block_impl_0这个函数传的值是self,上面是__Person__test_block_impl_0函数可以看到函数内部创建了一个person,来保存这个self指针。
static void _I_Person_test(Person * self, SEL _cmd) {
void (* block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344));
// 调用block
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
问题:如果是访问_name呢?
- (void)test
{
void (^ block)(void) = ^{
NSLog(@"self:%p",_name);
};
block();
}
答案是会捕获,这个相当于是self->_age; 这个self是在block的局部的,这个捕获的是self,然后访问的self里面的_age成员变量。并不是单独对_age进行捕获,而是对self进行捕获。看源码
问题:如果是[self name];
这个也是捕获的,因为先要拿到self,才能发消息
这里需要知道一个问题:self 是局部变量,不是全局变量!!!!
2:block类型
从下面代码的isa可以看出block是有类型的
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
Person *self;
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock; // block的类型
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
block就是一个oc对象。我们可以通过[block class] 来查看一下它的类型。
我们来打印一下 这里是arc环境下的,跟mrc还是不一样的
int main(int argc, char * argv[]) {
@autoreleasepool {
void (^ block)(void) = ^{
};
block();
NSLog(@"%@", [block class]);
NSLog(@"%@", [[block class] superclass]);
NSLog(@"%@", [[[block class] superclass] superclass]);
NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
结果 :印证了 block是一个oc对象,isa本质上来说是从NSObject对象中来的。
// __NSGlobalBlock__ : __NSGlobalBlock : NSBlock : NSObject
2018-08-17 20:04:39.088 newxc[17439:5526253] __NSGlobalBlock__
2018-08-17 20:04:39.089 newxc[17439:5526253] __NSGlobalBlock
2018-08-17 20:04:39.090 newxc[17439:5526253] NSBlock
2018-08-17 20:04:39.090 newxc[17439:5526253] NSObject
block到底有哪几种呢
// Global:没有访问auto变量。包括static 都是global
void (^ block)(void) = ^{
NSLog(@"self:");
};
// Stack:访问了auto变量。 :这个需要把arc关掉。
int ne = 10;
void (^ block1)(void) = ^{
NSLog(@"self:%d",ne);
};
NSLog(@"%@ %@ %@",[block class], [block1 class],[^{
NSLog(@"%d",ne);
} class]);
block();
2018-08-17 20:12:12.046 newxc[17492:5539840] __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__
但是通过编译到.cpp文件中查看跟上面的类型不一致。isa都是_NSConcreteStackBlock类型
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) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
int ne;
__main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _ne, int flags=0) : ne(_ne) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __main_block_impl_2 {
struct __block_impl impl;
struct __main_block_desc_2* Desc;
int ne;
__main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, int _ne, int flags=0) : ne(_ne) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
不一致原因:
一切以运行时结果为准。
有时候通过clang 转成的c++代码,有时候并不是真正转成的c++代码。(苹果的llvm1.0不再是生成c++文件,而是生成中间代码,但是大致上相同,只有少数不同。llvm编译器的一部分就是clang,就是clang属于llvm编译器里面的一部分)从上到下是高地址到低地址。
2.1:问题 :mrc环境下 下面讲的都是mrc环境下,会真实很多
先看一下block的类型
看下面的问题
void (^block)(void);
void test() {
// stack
int age = 10;
block = ^ {
NSLog(@"block------%d",age);
};
}
int main(int argc, char * argv[]) {
@autoreleasepool {
test();
block();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
显然:这个block的结果会有问题
2018-08-18 20:50:15.110 newxc[18572:6009580] block------272632489
那为什么会这样呢? 是因为这个block函数在栈上,block在全局变量区,但是这个block代码块内的所有包括这个age都是在栈上,栈有一个特点就是随时可能就会被释放,所以,这个打印的结果是不确定的。那如何解决呢?把这个block放在堆里。也即是把这个block变成malloc类型。调用copy。下面这个就是在堆上的就是block。
void test() {
// stack
int age = 10;
block = [^{
NSLog(@"block------%d",age);
} copy];
}
那如果是global类型调用copy呢 是什么类型呢
block = [^{
//NSLog(@"block------%d",age);
} copy];
block();
NSLog(@"block:%@",[block class]);
答案还是global类型
2018-08-18 21:04:59.322 newxc[18618:6028439] block:__NSGlobalBlock__
那上面stack调用两次copy呢?(调用一次在堆上,再调用一次呢)
block = [[^{
NSLog(@"block------%d",age);
} copy] copy];
答案还是malloc:也即是malloc调用copy还是在堆区,但是并不会再开辟内存了,而是引用计数器+1,但是这种的记住要自己管理内存。
2018-08-18 21:07:08.597 newxc[18641:6032580] block:__NSMallocBlock__
2.2:在arc下,block 自动加上copy的情况---:返回block。
typedef void (^block)(void);
block test() {
// stack
int age = 19;
return ^{
NSLog(@"block------%d",age);
};
}
int main(int argc, char * argv[]) {
@autoreleasepool {
block bloc = test();
bloc();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
因为test函数内return的block是栈上的block,一旦函数作用域消失,那这个block就消失了,所以不是很妥当。如果是arc环境下,会自动给返回的这个block加了一个copy操作。在你函数作用域消失的时候,会自动给你加上release操作。
2.3:在arc下,block 自动加上copy的情况---:强指针__block;
在arc环境下,只要被强指针持有,就是malloc。就会自动给你这个block加copy。
int age = 10;
block bloccc = ^{
NSLog(@"---%d",age);
};
NSLog(@"%@",[bloccc class]);
2018-08-19 17:10:03.248 newxc[19403:6283487] __NSMallocBlock__
但是在mrc环境下 就是
2018-08-19 17:12:09.420 newxc[19450:6287484] __NSStackBlock__
2.4:arc环境下:方法名中有usingBlock的方法参数时,也会进行copy操作。
NSArray *array = @[];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
有usingBlock 这个“^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { }”block也会搬到堆上面去。
2.5:在arc下,block 自动加上copy的情况---:block作为GCD的方法参数。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
只要是CGD里面的block,“ ^{ }”这个block就会自动添加copy操作。
so:小总结
在mrc下一定要用copy,在arc下可以用strong也可以用copy,用strong是因为有强指针,就会被自动添加copy,但是为了项目在哪里都能用,所以建议用copy。
3:对象类型的auto变量
arc环境
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.age = 10;
block bloccc = ^{
NSLog(@"---%d",person.age);
};
NSLog(@"-----=====");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
源码是
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;
}
};
这里是auto变量是什么捕捉的就是什么。如果是 static Person *person; 那么源码中捕捉到的就是Person **person.
因为整个block在堆上,而内部是对person的指针指引,所以只要block不释放,对person的指针就不会释放,person也就不会被释放。
3.1:假设在mrc环境下
int main(int argc, char * argv[]) {
@autoreleasepool {
block blo;
{
Person *person = [[Person alloc] init];
person.age = 10;
blo = ^{
NSLog(@"---%d",person.age);
};
[person release];
}
NSLog(@"========");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
#import "Person.h"
@implementation Person
- (void)dealloc
{
[super dealloc];
NSLog(@"person-dealloc----");
}
@end
2018-08-19 17:58:05.680 newxc[19773:6348592] person-dealloc----
2018-08-19 17:58:05.681 newxc[19773:6348592] ========
为啥呢:因为这个block是在栈上,而且栈空间上的是不会持有对象的。如果是堆空间,是有能力保住这个对象的。所以person就dealloc了。
如果对这个block copy 那么person就不会dealloc了,因为block相当于对person做了retain操作。
假设在arc环境下----------这里2.1.1会做详细解释,从这里做案例开始.
nt main(int argc, char * argv[]) {
@autoreleasepool {
block blo;
{
Person *person = [[Person alloc] init];
person.age = 10;
blo = ^{
NSLog(@"---%d",person.age);
};
}
NSLog(@"========");
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-08-19 18:12:54.016 newxc[19963:6373020] ========
2018-08-19 18:12:54.017 newxc[19963:6373020] person-dealloc----
会先出了作用域,block释放,然后block对其person的指针也没了,所以person释放。
假设用__weak来修饰person实例
int main(int argc, char * argv[]) {
@autoreleasepool {
block blo;
{
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *wekaPerson = person;
blo = ^{
NSLog(@"---%d",wekaPerson.age);
};
}
NSLog(@"========");
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-08-19 18:15:29.656 newxc[19986:6377817] person-dealloc----
2018-08-19 18:15:29.660 newxc[19986:6377817] ========
看结果不一样了,为啥呢,
3.2:arc:对上面做一个原因解释
弱引用:需要运行时的支持的。
Person *person = [[Person alloc] init];
person.age = 10;
__weak Person *wekaPerson = person;
blo = ^{
NSLog(@"---%d",wekaPerson.age);
};
你会发现,当你写了__weak,在去调用下面的命令时会报错
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
/var/folders/27/tr3mg74j3nq_zxgdyg1rj6480000gn/T/main-6a8820.mi:47464:28: error:
cannot create __weak reference because the current deployment target does
not support weak references
__attribute__((objc_ownership(weak))) Person *wekaPerson = person;
^
1 error generated.
你会发现上述报错,只是静态的编译会报错,所以需要我们指定是runtime并且指定arc环境。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
发现,不一样的地方,就是person弱引用了
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__weak wekaPerson;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _wekaPerson, int flags=0) : wekaPerson(_wekaPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
那如果是不加__weak呢 ,这里注意要接着用上面的指令编译,发现结果,是strong.
typedef void (*Block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong per; //注意这里,是strong类型,这是没有加__weak的情况,同样也没有加__strong
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
那如果是加了__strong的情况呢?接着使用上指令 结果可以看到依然是__strong修饰
Block blo;
{
__strong Person *per = [[Person alloc] init];
per.age = 100;
blo = ^{
NSLog(@"---%d",per.age);
};
};
typedef void (*Block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong per; // 主要看这里,是用__strong来修饰的
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
Person *__strong per = __cself->per; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_a9de8d_mi_0,((int (*)(id, SEL))(void *)objc_msgSend)((id)per, 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->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 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};
好总结一下,
如果block在栈上(不论是arc还是mrc),都不会对block内部的产生强引用。(因为自己都是栈上的,随时会被销毁,所以不会对里面的对象产生强引用。)
可以看到这个有对象的block跟以前的block不太一样。不太理解不一样的,可以看1.1捕获上面的源码跟这个的区别,是多了两个函数,请看下面。
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 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};
__main_block_copy_0赋值给copy函数,__main_block_dispose_0赋值给dispose函数。
也就是说,上面的blo一旦进行copy操作,要copy到堆上,就会自动调用copy函数,这个函数会调用里面的_Block_object_assign函数,这个函数做的事情是:会根据外部auto变量传进来的是strong还是weak,来对__main_block_impl_0里面的Person指针进行对外部的person对象是strong还是weak。如果是传进来的是strong,则__main_block_impl_0里面的person指针会强引用着外部的person对象,如果是weak也是一样。
那dispose什么时候调用呢? 当堆上的blo 进行销毁时,就会自动调用block内部的dispose,就会释放引用的那些函数
看总结图
看题
苹果会认为block内部有强引用的时候,不会释放,等到强引用释放完了才会释放。弱引用没有影响。
额外提示一下,什么情况下才会出现copy和dispose这两个函数呢?
一旦block内部访问的是对象类型的,就会出现copy和dispose函数,因为访问的是对象,想拥有这个对象,所以就会有内存对象管理。
4:如何修改block内部的变量:__block、static、全局、指针
如果我们想对block里面的变量进行修改,怎么做?
a:static
b:弄成全局变量
c:用__block.
d:数据类型用指针指向(参考static使用的block内部原理)
1:先看d也就是static使用的block的内部原理
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int height = 10;
Block bl = ^{
height = 199;
};
bl();
}
return 0;
}
typedef void(*Block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *height;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_height, int *__newhei, int flags=0) : height(_height){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *height = __cself->height; // bound by copy
(*height) = 199;
}
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 argc, const char * argv[]) {
static int height = 10;
Block bl = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &height));
((void (*)(__block_impl *))((__block_impl *)bl)->FuncPtr)((__block_impl *)bl);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
从上面可以看出来 传进去的是height的指针,从__main_block_impl_0对象结构体中也可以知道,是拿int *height;来接收的,在__main_block_func_0函数中可以看到赋值时候用的是int *_newhei = __cself->_newhei; *_newhei = 20; .所以我们可以这么做
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
int *_hei = &age;
int *_newhei = _hei;
*_newhei = 304;
Block bl = ^{
*_newhei = 20;
};
bl();
NSLog(@"在这里打印的话会发现age已经变为20 了 并且*_newhei 也是20")
}
return 0;
}
当然 这种方式是麻烦了点,而且只针对基本数据类型,对于对象类型不适用。那为什么不适用呢,请看下面
Person *per = [[Person alloc] init];
NSInteger df = 19;
static int lage = 100;
Block bl = ^{
lage = 300;
NSLog(@"%@",per.name);
};
bl();
这个底层源码是 我们只看这一段
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *lage = __cself->lage; // bound by copy
Person *per = __cself->per; // bound by copy
(*lage) = 300;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_70_0rj_h7dx3zv20xx9ln3x0d8h0000gn_T_main_3efd6b_mi_0,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)per, sel_registerName("name")));
}
注意到,对于基本数据类型,这里取值都用的是*lage 而不是lage,也就是这是取值,并不是取的指针。
但是对于对象来说,取的是per,也就是(id)per,而不是*per,(这个*per在oc中也不代表什么)。所以对于oc对象来说,同样跟c语言一样有指针,但是方式却不一样。 所以oc对象来说 应该方式都是这样的下面包装一层的方式
2:看一个例子 用__block
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int age = 10;
Block blo = ^{
NSLog(@"---%d",age);
};
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
typedef void (*Block)(void);
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding; // 看下面的引用知道,这个指向自己
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__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;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_0dc06e_mi_0,(age->__forwarding->age));
}
// 要对__main_block_impl_0对象进行内存管理,所有有copy和dispose
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 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(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// 这里是 __block int age = 10; 是下面的这个
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
10
};
Block blo = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
// 要对__Block_byref_age_0对象进行内存管理,所有有copy和dispose
看这个结构体 这个是调用age,能看出来第二个(__Block_byref_age_0 *)&age赋值给了__Block_byref_age_0结构体的forwarding指针,也就是把自己的地址赋值给了自己结构体中的forwarding。
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
10};
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
接着看这个,看blo这个结构体把age的地址:&age 传进去了,赋值给了第三个 __Block_byref_age_0 *age; // by ref
Block blo = (
(void (*)())&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_age_0 *)&age,
570425344));
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__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;
Desc = desc;
}
};
所以,block内部会有一个指针(这里是*age)指向__Block_byref_age_0结构体,这个结构体内部又有age这个值,存储这个值,那是如何修改这个值的呢?
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_acce32_mi_0,(age->__forwarding->age));
}
看这个__cself从__main_block_impl_0这个结构体中拿到age也就是__Block_byref_age_0结构体,然后用这个结构体先访问自己内部的forwarding,因为forwarding指向的就是自己,所以再拿age,再赋值,就赋值成功啦。那到此肯定有疑问,这个改变的不就是block内部的age这个值么,而且这个age是在内部创建的,验证。打印一下
__block int age = 10;
Block blo = ^{
age = 20;
NSLog(@"---%d",age);
};
blo();
NSLog(@"外面:%d",age);
2018-09-07 22:01:44.772 newxc[31713:10543714] ---20
2018-09-07 22:02:16.602 newxc[31713:10543714] 外面:20
那是为啥呢?看这个,不加block,只用__block修饰,再看源码
int main(int argc, char * argv[]) {
@autoreleasepool {
__block int age = 10;
NSLog(@"外面:%d",age);
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
NSLog((NSString *)&__NSConstantStringImpl__var_folders_27_tr3mg74j3nq_zxgdyg1rj6480000gn_T_main_80e809_mi_0,(age.__forwarding->age));
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
请看,有__Block_byref_age_0结构体,就是说只要加上__block,就会被包装秤这个对象结构体.在nslog中访问的就是__Block_byref_age_0结构体中的age,所以是能修改的,这个原来船进入的是10,并不是10的地址,而是这个结构体中的age是新生成的age变量,这只是造成了一个假象,显示这个age被修改了,其实是修改的这个__Block_byref_age_0结构体对象里面的age变量,验证如下 (就如同,外面是一个person变量,有一个age的属性,在block内部给age赋值,是修改成功的)。
当使用了__block之后,age的地址就发生了变化,这里因为arc环境下,block已经copy到堆上,但是原来__block修饰的age的那个对象还在栈上,而最后age= 20,这个age,是访问的堆上的age,(因为已经从栈到堆了。)
总结
那如果再加上一个person对象呢用__block修饰?
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block Person *per = [[Person alloc] init];
__block int age = 10;
Block bl = ^{
NSString *a = per.name;
per.name = @"34";
age = 300;
};
bl();
}
return 0;
}
typedef void(*Block)(void);
struct __Block_byref_per_0 {
void *__isa;
__Block_byref_per_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *per;
};
struct __Block_byref_age_1 {
void *__isa;
__Block_byref_age_1 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_per_0 *per; // by ref
__Block_byref_age_1 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_per_0 *_per, __Block_byref_age_1 *_age, int flags=0) : per(_per->__forwarding), age(_age->__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_per_0 *per = __cself->per; // bound by ref
__Block_byref_age_1 *age = __cself->age; // bound by ref
NSString *a = ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)(per->__forwarding->per), sel_registerName("name"));
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)(per->__forwarding->per), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_70_0rj_h7dx3zv20xx9ln3x0d8h0000gn_T_main_b0ec3f_mi_0);
(age->__forwarding->age) = 300;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->per, (void*)src->per, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->per, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->age, 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(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_per_0 per = {(void*)0,(__Block_byref_per_0 *)&per, 33554432, sizeof(__Block_byref_per_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))) __Block_byref_age_1 age = {(void*)0,(__Block_byref_age_1 *)&age, 0, sizeof(__Block_byref_age_1), 10};
Block bl = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_per_0 *)&per, (__Block_byref_age_1 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)bl)->FuncPtr)((__block_impl *)bl);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
看上面就会生成两个__main_block_impl_结构体,并且最后nslog里,访问的也是__main_block_impl_结构体里面的forwarding里的per或者age。
4.1:__block的内存管理
(如果是对象修饰用__block的话,记住两个copy是不同的,__main_block_copy_0和__Block_byref_id_object_copy。作用对象也不同)
强引用__Block_byref_age_0 *age; 对象,对这个强引用(copy)
销毁对__Block_byref_age_0 *age;对象进行release。
(这个内存管理对没有修饰的对象类型的copy是一样的,如果加上了__block, 那么就是再加上一层内存管理,也就是__block变量内部对对象类型的内存管理。)
4.2:__forwarding指针原理
那上面间接的可以解释为什么有__forwarding指针,而不是age直接指向age
(age->__forwarding->age) = 20;
因为这个__Block_byref_age_0对象刚开始都是在栈上,当block被copy到堆上的时候,自动也就copy了一份__Block_byref_age_0对象,这个时候,第一个age是栈上的,而它的__forwarding指针是指向堆上的它自己,所以需要用__forwarding的age来取值或者赋值,但是如果block是在栈上,那么这些都不会被copy到堆上,所以__forwarding还是指向自己,所以,这不论是在栈还是再堆都实现了指向自己,所以可以赋值或者取值。(copy到堆上了,地址肯定就发生变化了。)
4.3:问题:blok内部会对下面两个对象有什么不同么
Person *per = [[Person alloc] init];
__block int age = 10;
不同点在这里
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->per, (void*)src->per, 3/*BLOCK_FIELD_IS_OBJECT*/);}
一旦用__block,这个assign函数就会对这个对象产生强引用,而没有用__block的对象,会根据是strong还是weak来修饰的,再对它产生强或者弱引用。
那相同点是什么呢?
4.4:被__block修饰的对象类型:arc环境下
这里着重看一下下面两个
__block Person *weakPerson = per;
__block __weak Person *weakPerson = per;
这两个在代码上有需要注意的地方,先看下__block Person *weakPerson = per;的源码
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];
__block Person *weakPerson = per;
Block blok = ^{
weakPerson.age = 20;
};
blok();
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
#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);
} // 看这里assign 对 person这个对象进行copy,也就是进行131,根据外面传进来的操作。
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
typedef void (*Block)(void);
struct __Block_byref_weakPerson_0 {
void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong weakPerson;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : 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_weakPerson_0 *weakPerson = __cself->weakPerson; // bound by ref
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)(weakPerson->__forwarding->weakPerson), sel_registerName("setAge:"), 20);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_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->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(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Person *per = ((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))) __Block_byref_weakPerson_0 weakPerson = {
(void*)0,
(__Block_byref_weakPerson_0 *)&weakPerson,
33554432,
sizeof(__Block_byref_weakPerson_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
per}; // 这里的封装好的weakPerson放在下面里面.
Block blok = (
(void (*)())&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_weakPerson_0 *)&weakPerson,
570425344));
((void (*)(__block_impl *))((__block_impl *)blok)->FuncPtr)((__block_impl *)blok);
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
看这个用__block修饰的对象跟__block修饰的普通数据类型不一样的地方,在__Block_byref_weakPerson_0对象里多了两个参数, void (*__Block_byref_id_object_copy)(void*, void*);和 void (*__Block_byref_id_object_dispose)(void*);那这两个的作用是什么呢?就是管理__Block_byref_per_0中_Person *__strong weakPerson;的内存的,也就是是对象就需要的内存管理。
接着分析这个过程,刚开始把weakPerson封装好给blok赋值,从__Block_byref_weakPerson_0结构中可以看到,赋值的内容:isa是指针,
__forwarding是赋值的地址&weakPerson,
__flags赋值的是33554432,
__size赋值的是sizeof(__Block_byref_weakPerson_0),
__Block_byref_id_object_copy赋值的是 __Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose赋值的是__Block_byref_id_object_dispose_131,
Person *__strong weakPerson;赋值的是per。
所以__forwarding指向的就是自己。而__Block_byref_id_object_copy_131看这个函数内是这个_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131); 这个是这个结构体指针的地址加40,也就是__Block_byref_weakPerson_0的地址加40 就是 Person *__strong weakPerson;,对它进行的copy和dispose操作。
所以 过程是:
当block被copy到堆上的时候,(arc环境下),block会调用__main_block_copy_0函数,来对__main_block_impl_0对象内部的 __Block_byref_weakPerson_0 *weakPerson;进行copy,进行强指针引用。同时__Block_byref_weakPerson_0对象也被拷贝到堆上,在这个copy的时候,__Block_byref_weakPerson_0对象就会调用自己内部的__Block_byref_id_object_copy对自己内部的对象 Person *__strong weakPerson进行copy;根据前面的修饰符进行强引用还是弱引用,这里因为是strong,所以是强引用。同时注意一下,如果是mrc环境下,那么这个对person的引用就永远不会是强引用。
好下面看
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];
__block __weak Person *weakPerson = per;
Block blok = ^{
weakPerson.age = 20;
};
blok();
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
typedef void (*Block)(void);
struct __Block_byref_weakPerson_0 {
void *__isa;
__Block_byref_weakPerson_0 *__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_weakPerson_0 *weakPerson; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : 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_weakPerson_0 *weakPerson = __cself->weakPerson; // bound by ref
((void (*)(id, SEL, int))(void *)objc_msgSend)((id)(weakPerson->__forwarding->weakPerson), sel_registerName("setAge:"), 20);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_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->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(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Person *per = ((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_0 weakPerson = {(void*)0,(__Block_byref_weakPerson_0 *)&weakPerson, 33554432, sizeof(__Block_byref_weakPerson_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, per};
Block blok = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));
((void (*)(__block_impl *))((__block_impl *)blok)->FuncPtr)((__block_impl *)blok);
}
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
跟上面不同的点就在于__Block_byref_weakPerson_0里面的 Person *__weak weakPerson;这里是weak修饰,同样形象化的以下图解释
证明是强指针:
证明有弱指针
在mrc环境下,证明copy也不会产生强指针。
arc下不论block里经过几层对person对象,都会有强弱指针。
block的内存管理,调用_Block_object_assign的时候传进去的值要么是3要么是8,但是__block修饰的 传进去的是__Block_byref_id_object_copy_131 这个 是131,这个管理的还是不同的。
总结:
5:循环引用
typedef void (^Block)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];
per.age = 10;
per.block = ^{
NSLog(@"-----%d",per.age);
};
}
NSLog(@"-----000000000");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 我们只看这一节
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong per;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _per, int flags=0) : per(_per) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
看到上面是__strong,也就是下面这个图
导致最后person对Person这个类的指针释放了,上面两个却一直相互引用,无法释放。
5.1:解决循环引用问题
arc:
__weak __unsafe_unretained解决 和__block
Person *per = [[Person alloc] init];
__weak typeof(per) weakPer = per;
per.age = 10;
per.block = ^{
NSLog(@"-----%d",weakPer.age);
};
Person *per = [[Person alloc] init];
// __weak 不会产生强引用
// __unsafe_unretained 不会产生强引用 不安全
__unsafe_unretained typeof(per) weakPer = per;
per.age = 10;
per.block = ^{
NSLog(@"-----%d",weakPer.age);
};
weak会把指向的对象销毁时会自动置让指针为nil,所以weak是安全的。
__block
int main(int argc, char * argv[]) {
@autoreleasepool {
__block Person *per = [[Person alloc] init];
// __weak 不会产生强引用
// __unsafe_unretained 不会产生强引用 不安全
per.age = 10;
per.block = ^{
NSLog(@"-----%d",per.age);
per = nil; // 这两个必须要写
};
per.block(); // 这两个必须要写 可能有内存泄漏,
}
NSLog(@"-----000000000");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
mrc环境下:
__unsafe_unretained 和__block
mrc不支持__weak。
int main(int argc, char * argv[]) {
@autoreleasepool {
__unsafe_unretained Person *per = [[Person alloc] init];
per.age = 10;
per.block = [^{
NSLog(@"-----%d",per.age);
} copy];
[per release];
}
NSLog(@"-----000000000");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
2018-09-10 19:42:27.889 newxc[34540:11722615] person-dealloc----
2018-09-10 19:42:27.890 newxc[34540:11722615] -----000000000
__block在mrc下不会产生强引用对立面的对象。
__block Person *per = [[Person alloc] init];
per.age = 10;
per.block = [^{
NSLog(@"-----%d",per.age);
} copy];
[per release];
大总结:面试
1:block的原理是怎样的,本质是什么?
封装了函数调用以及调用环境OC对象。
2:__block的作用?有什么使用的注意点?
封装成了一个对象,在mrc环境下,不会产生强引用对立面的对象
3:block的属性修饰词为什么是copy?使用block有哪些使用注意?
block没有copy,就不会在堆上,copy到堆上,就可以进行内存管理了,使用注意:循环引用
4:block在修改NSMUtablearray,需不需要添加__block?
不需要,
- (void)test
{
__weak typeof(self) weakSelf = self;
self.age = 10;
self.block = ^{
NSLog(@"-----%d",weakSelf->_age);
};
}
这个会报错,可能是会空值,不能产生强引用,可能会空值
所以就要用一个强指针,来骗编译器,这是编译器行为
- (void)test
{
__weak typeof(self) weakSelf = self;
self.age = 10;
self.block = ^{
__strong typeof(weakSelf) myself = weakSelf;
NSLog(@"-----%d",myself->_age);
};
}
另外 用这个强指针 可以确保不会保住这个对象。
推荐阅读
-
iOS Block的内存管理
-
ios Block
-
iOS Block与循环引用
-
微信浏览器弹出框滑动时页面跟着滑动的实现代码(兼容Android和IOS端)
-
Import block visibility abstract 自定义配置(AndroidStudio4.0)
-
ios游戏开发 Sprite Kit教程:初学者 2 iosios游戏开发spritekitcocos2d
-
IOS游戏引擎列表 博客分类: Coco2d-x cocos2d
-
基于cocos2d开发ios游戏步骤一 博客分类: cocos2d-iphone屌丝 cocos2diosiphoneipaditunes
-
php、java、android、ios通用的3des方法(推荐)
-
Android开发中实现IOS风格底部选择器(支持时间 日期 自定义)