block种类
1.__NSGlobalBlock__
block无参无返回值
void(^block)(void) = ^{
NSLog(@"aaaaaaa");
};
NSLog(@"%@", block);
aaa[6077:155418] <__NSGlobalBlock__: 0x10d32e0e0>
2.__NSMallocBlock__
block访问外界变量
int a = 100;
void(^block)(void) = ^{
NSLog(@"aaaaa - %d", a);
};
NSLog(@"%@", block);
aaa[6346:163587] <__NSMallocBlock__: 0x6000013a9a10>
3.__NSStackBlock__
block访问外界变量,同时用weak弱引用
int a = 100;
void(^__weak block)(void) = ^{
NSLog(@"aaaaa - %d", a);
};
NSLog(@"%@", block);
aaa[6485:167690] <__NSStackBlock__: 0x7ff7b7e3bae8>
总结
- block没访问外界变量则直接存储在全局区
- block访问外界变量,强引用,存储在堆区
- block访问外界变量,弱引用,存储在栈区
循环引用
//代码一
NSString *name = @"AAA";
self.block = ^(void){
NSLog(@"%@",self.name);
};
self.block();
//代码二
UIView animateWithDuration:1 animations:^{
NSLog(@"%@",self.name);
};
代码1中,因为self持有block,block访问了self.name导致block也持有了self,从而self和block互相持有,所以代码1发生了循环引用。 代码二不构成相互持有。下面介绍几种方法解决循环引用:
1.weak-stong-dance
__weak typeof(self) weakSelf = self;
self.slBlock = ^(void){
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
self.slBlock();
__weak修饰self,打破self对block的强引用 ,如果block里边嵌套block,需同时使用__weak和__strong
2.__block修饰变量
__block ViewController *vc = self;
self.slBlock = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
vc = nil;//手动释放
});
};
self.sllBlock();
block必须要调用,否则依旧存在循环引用
3.self作为block参数
self.slBlock = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
});
};
self.slBlock(self);
4.NSProxy 虚拟类
NSProxy可实现伪多继承,参考YYkit的YYWeak,NSProxy就解决了NSTimer和CADisplayLink
创建时对self强引用
问题。NSProx就像一个代理人,可通过继承它,并重写消息慢速转发的那两个方法(forwardInvocation、methodSignatureForSelector)实现消息转发
自定义一个NSProxy
@interface SLProxy : NSProxy
- (id)transformObjc:(NSObject *)objc;
+ (instancetype)proxyWithObjc:(id)objc;
@end
@interface SLProxy()
@property(nonatomic, weak, readonly) NSObject *objc;
@end
@implementation SLProxy
- (id)transformObjc:(NSObject *)objc{
_objc = objc;
return self;
}
+ (instancetype)proxyWithObjc:(id)objc{
return [[self alloc] transformObjc:objc];
}
- (BOOL)respondsToSelector:(SEL)aSelector{
return [self.objc respondsToSelector:aSelector];
}
//1、查询该方法的方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
NSMethodSignature *signature;
if (self.objc) {
signature = [self.objc methodSignatureForSelector:sel];
}else{
signature = [super methodSignatureForSelector:sel];
}
return signature;
}
//2.有了方法签名之后就会调用方法实现
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = [invocation selector];
if ([self.objc respondsToSelector:sel]) {
[invocation invokeWithTarget:self.objc];
}
}
自定义Cat、Bird类
#import "ViewController.h"
#import "SLProxy.h"
//********Cat类********
@interface Cat : NSObject
@end
@implementation Cat
- (void)eat{
NSLog(@"我爱吃鱼");
}
@end
//********Bird类********
@interface Bird : NSObject
@end
@implementation Bird
- (void)fly{
NSLog(@"我会飞");
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self cjl_proxyTest];
}
- (void)cjl_proxyTest{
Bird *bird = [[Bird alloc] init];
Cat *cat = [[Cat alloc] init];
SLProxy *proxy = [SLProxy alloc];
[proxy transformObjc:cat];
[proxy performSelector:@selector(eat)];
[proxy transformObjc:bird];
[proxy performSelector:@selector(fly)];
}
@end
SLProxy既爱吃鱼,也可以飞,具备了猫和鸟的能力
通过NSProxy解决定时器self得强引用
self.timer = [NSTimer timerWithTimeInterval:1 target:[SLProxy proxyWithObjc:self] selector:@selector(print) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
Block底层
定义一个block
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)(void) = ^{
printf("aaaaa");
};
}
编译成cpp后结果如下
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
printf("aaaaa");
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
}
// @end
相当于block
等于__ViewController__viewDidLoad_block_impl_0
,是一个函数。
查看__ViewController__viewDidLoad_block_impl_0定义,是一个结构体,
同时可以说明block是一个__ViewController__viewDidLoad_block_impl_0
类型的对象,这也是为什么block
能够%@
打印的原因。block
的本质
是对象、函数、结构体
,由于block函数没有名称,也被称为匿名函数。
1.block是需要调用的
函数声明:block内部实现声明成了一个函数__ViewController__viewDidLoad_block_func_0
执行具体函数实现:通过调用block的FuncPtr
指针,调用block执行
2.block捕获外界变量
- (void)viewDidLoad {
[super viewDidLoad];
int t = 10;
void(^block)(void) = ^{
printf("aaaaa - %d",t);
};
block();
}
编译成cpp
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
int t;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int _t, int flags=0) : t(_t) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
int t = __cself->t; // bound by copy
printf("aaaaa - %d",t);
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
int t = 10;
void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, t));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
可知block捕获外界变量时,会在内部会自动生成同一个属性来保存
如果外界变量t用__block修饰,然后在block中对t进行修改,编译如下
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
__Block_byref_t_0 *t; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_t_0 *_t, int flags=0) : t(_t->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
__Block_byref_t_0 *t = __cself->t; // bound by ref
(t->__forwarding->t)++;
printf("aaaaa - %d",(t->__forwarding->t));
}
。。。
// @end
外界变量
会生成__Block_byref_a_0
结构体,用来保存原始变量指针和值,在block内部即可对外部变量进行操作(即指针拷贝)。
Block最底层真正类型
查看libclosure源码,可知block真正的类型,是一个结构体。
// CJL注释:Block 结构体
struct Block_layout {
//指向表明block类型的类
void *isa;//8字节
//用来作标识符的,类似于isa中的位域,按bit位表示一些block的附加信息
volatile int32_t flags; // contains ref count 4字节
//保留信息,可以理解预留位置,用于存储block内部变量信息
int32_t reserved;//4字节
//函数指针,指向具体的block实现的调用地址
BlockInvokeFunction invoke;
//block的附加信息
struct Block_descriptor_1 *descriptor;
// imported variables
};