block(三)
2019-03-21 本文已影响11人
dandelionYD
引言
我们在前面知道了block内部捕获外部的变量blcok(一)、blcok(二)
block_07.png发现修改不了被捕获的变量
解决:
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
void (^myBlock)(void) = ^{
age = 20;
NSLog(@"age = %d",age);
};
myBlock();
NSLog(@"age = %d",age);
}
return 0;
}
打印:
12.__block的使用[34938:11235669] age = 20
12.__block的使用[34938:11235669] age = 20
- __block的作用:用于解决block内部无法修改auto变量值的问题
- 注意:__block不能修饰全局变量、静态变量(static)
我们看下底层的c++源码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
block_08.png底层:编译器会将__block变量包装成一个对象
#import <Foundation/Foundation.h>
typedef void(^myBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *arr = [NSMutableArray array];
myBlock block = ^{
[arr addObject:@"1"];
[arr addObject:@"2"];
};
block();
NSLog(@"arr = %@",arr);
}
return 0;
}
打印:
13.__block的使用2[35103:11287535] arr = (
1,
2
)
不会报错:因为只是来用arr的,而不是来修改arr的(arr = nil)
说明:block在修改NSMutableArray,需不需要添加__block?
为数组增删改的时候不需要
修改指针的对象的时候需要
__block的细节
#import <Foundation/Foundation.h>
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
struct __Block_byref_age_0 {
void *__isa; // 8
struct __Block_byref_age_0 *__forwarding; //8
int __flags; //4
int __size; //4
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
struct __Block_byref_age_0 *age; // by ref
};
typedef void(^myBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
myBlock block = ^{
age = 20;
NSLog(@"%d",age);
};
block();
struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0*)block;
NSLog(@"%p",&age);
NSLog(@"End"); //打个断点
}
return 0;
}
打印:
14._ _block细节[35269:11308052] 20
14._ _block细节[35269:11308052] 0x100711318
(lldb) p/x blockImpl->age
(__Block_byref_age_0 *) $0 = 0x0000000100711300
(lldb) p/x &(blockImpl->age->age)
(int *) $1 = 0x0000000100711318
(lldb) p/x &age
(int *) $2 = 0x0000000100711318
(lldb)
发现:age的地址就是内部结构体的age地址
&age == &(blockImpl->age->age)
(blockImpl->age)的地址 + 8 + 8 + 4 +4 = (blockImpl->age->age)的地址值
__block的内存管理
- 当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的_forwarding指针
还是以【上述__block细节】代码
我们发现
block_11.png_ _ block的_forwarding指针:指向的是 自身的isa
block_12.png对象类型的auto变量、__block变量
-
当block在栈上时,对他们不会产生强引用
-
当block拷贝到堆上的时,都会通过copy函数来处理他们
- __block变量(假设为变量a)
- _Block _object _ assigin ( (void*)&dst->a , (void*)src->a,8/ *BLOCK_FIELD_IS_BYREF*/);
- 对象类型的auto变量(假设为变量p)
- _Block _object _ assigin ( (void*)&dst->p , (void*)src->p,3/ *BLOCK_FIELD_IS__OBJECT*/);
- __block变量(假设为变量a)
-
当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变量(假设为变量a)
对象 | BLOCK_FIELD_IS__OBJECT |
---|---|
__block变量 | BLOCK_FIELD_IS_BYREF |
被__block修饰的对象类型
- 当__blcok变量在栈上时,不会对指向的对象产生强引用
- 当__block变量被copy到堆上的时,
- 会调用__block变量内部的copy函数
- copy函数内部会调用_Block _object _assign函数
- Block object assign函数会根据所指向对象的修饰符( strong、_ weak、__ unsafe _unretained) 做出相应的操作,形成强引用(retain)或者弱引用(注意:这里只要RAC有retain,MRC不会retain)
- 如果__block变量从堆上移除
- 会调用__block内部的dispose函数
- dispose函数内部会调用_Block _object _ dispose函数
- _Block _object _ dispose函数会自动释放指向的对象(release)
#import <Foundation/Foundation.h>
@interface myPerson:NSObject
@end
@implementation myPerson
@end
typedef void(^myBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__block __weak myPerson *weakP = p;
myBlock blcok = ^{
NSLog(@"person = %@",weakP);
};
blcok();
}
return 0;
}
分析见下图:
block_13.png循环引用
我们看下下面的例子:
#import <Foundation/Foundation.h>
typedef void(^myBlock)(void);
@interface myPerson : NSObject
@property(nonatomic,copy)myBlock block;
@end
#import "myPerson.h"
@implementation myPerson
-(void)dealloc{
NSLog(@"%s",__FUNCTION__);
}
@end
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
p.block = ^{
NSLog(@"%@",p);
};
NSLog(@"End");
}
return 0;
}
发现:p对象并没有被销毁
block_14.png
分析:
p对象里面持有block,而block里面又持有person —>造成了循环引用啦
接下来我们来看看循环引用的解决:【ARC】
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__weak myPerson *weakP = p;
p.block = ^{
NSLog(@"%@",weakP);
};
NSLog(@"End");
}
return 0;
}
打印:
17.循环引用[35636:11420439] End
17.循环引用[35636:11420439] -[myPerson dealloc]
此时:person对象被释放了
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__weak typeof(p) weakP = p;
p.block = ^{
NSLog(@"%@",weakP);
};
NSLog(@"End");
}
return 0;
}
打印:
17.循环引用[35659:11425413] End
17.循环引用[35659:11425413] -[myPerson dealloc]
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__unsafe_unretained myPerson * weakP = p;
p.block = ^{
NSLog(@"%@",weakP);
};
NSLog(@"End");
}
return 0;
}
打印:
17.循环引用[35674:11429822] End
17.循环引用[35674:11429822] -[myPerson dealloc]
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__block myPerson * weakP = p;
p.block = ^{
NSLog(@"%@",weakP);
weakP = nil;
};
p.block();
NSLog(@"End");
}
return 0;
}
注意:必须要调用block才行
打印:
17.循环引用[35724:11431748] <myPerson: 0x1005053c0>
17.循环引用[35724:11431748] End
17.循环引用[35724:11431748] -[myPerson dealloc]
总结:
-
使用: __ weak、__ unsafe_unretained
- __ weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
- __ unsafe_unretained,不会产生强引用 ,不安全,指向的对象销毁时,指针存储的地址值不变
- 使用:__ block(必须要调用block并且要做置空处理)
解决循环引用:【MRC】
由于:MRC下不支持__weak
所以 :只有__ unsafe_unretained、__ block方法
mrc下不会对__block的对象进行强引用,所以mrc下不需要置位nil)
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__unsafe_unretained myPerson * weakP = p;
p.block = ^{
NSLog(@"%@",weakP);
};
NSLog(@"End");
}
return 0;
}
--------------
#import <Foundation/Foundation.h>
#import "myPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
myPerson *p = [[myPerson alloc]init];
__block myPerson * weakP = p;
p.block = ^{
NSLog(@"%@",weakP);
};
p.block();
NSLog(@"End");
}
return 0;
}
补充:__ weak 和 __ strong
myPerson.h
#import <Foundation/Foundation.h>
typedef void(^myBlock)(void);
@interface myPerson : NSObject
@property(nonatomic,copy)myBlock block;
-(void)run;
@end
myPerson.m
#import "myPerson.h"
@implementation myPerson
-(void)dealloc{
NSLog(@"%s",__FUNCTION__);
}
-(void)run{
NSLog(@"run----");
}
@end
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
myPerson *p = [[myPerson alloc]init];
__weak myPerson *weakP = p;
p.block = ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"%@",weakP);
[weakP run];
});
};
p.block();
NSLog(@"End");
}
打印:
block2[36791:11582899] End
block2[36791:11582899] -[myPerson dealloc]
block2[36791:11582959] (null)
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
myPerson *p = [[myPerson alloc]init];
__weak myPerson *weakP = p;
p.block = ^{
__strong myPerson *p = weakP;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"%@",p);
[p run];
});
};
p.block();
NSLog(@"End");
}
打印:
block2[36767:11581355] End
block2[36767:11581544] <myPerson: 0x600001fc6be0>
block2[36767:11581544] run----
block2[36767:11581544] -[myPerson dealloc]
分析见下图:
block_17.png block_18.png友情链接: