type of block
1.__NSGlobalBlock__
block has no parameters and no return value
void(^block)(void) = ^{
NSLog(@"aaaaaaa");
};
NSLog(@"%@", block);
aaa[6077:155418] <__NSGlobalBlock__: 0x10d32e0e0>
2.__NSMallocBlock__
block accesses external variables
int a = 100;
void(^block)(void) = ^{
NSLog(@"aaaaa - %d", a);
};
NSLog(@"%@", block);
aaa[6346:163587] <__NSMallocBlock__: 0x6000013a9a10>
3.__NSStackBlock__
Block accesses external variables while using weak weak references
int a = 100;
void(^__weak block)(void) = ^{
NSLog(@"aaaaa - %d", a);
};
NSLog(@"%@", block);
aaa[6485:167690] <__NSStackBlock__: 0x7ff7b7e3bae8>
Summarize
- If the block does not access external variables , it is directly stored in the global area
- Block accesses external variables , strong references, stored in the heap area
- Block accesses external variables , weak references, stored in the stack area
circular reference
//代码一
NSString *name = @"AAA";
self.block = ^(void){
NSLog(@"%@",self.name);
};
self.block();
//代码二
UIView animateWithDuration:1 animations:^{
NSLog(@"%@",self.name);
};
In code 1, because self holds the block, and the block accesses self.name, the block also holds self, so self and block hold each other , so a circular reference occurs in code 1. Code Two does not constitute mutual holding. Here are a few ways to resolve circular references:
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 modifies self to break self's strong reference to block. If block is nested inside block, you need to use __weak and __strong at the same time
2. __block modifier variables
__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();
The block must be called , otherwise there will still be a circular reference
3. self as a block parameter
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 virtual class
NSProxy can realize pseudo-multi-inheritance . Referring to YYWeak of YYkit, NSProxy solves the problem NSTimer和CADisplayLink
of creation 对self强引用
. NSProx is like an agent, which can realize message forwarding by inheriting it and rewriting the two methods (forwardInvocation, methodSignatureForSelector) for slow forwarding of messages
Customize a 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];
}
}
Customize Cat and Bird classes
#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 loves to eat fish and can also fly, possessing the abilities of cats and birds
Solve the strong reference of timer self through NSProxy
self.timer = [NSTimer timerWithTimeInterval:1 target:[SLProxy proxyWithObjc:self] selector:@selector(print) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
Block bottom layer
define a block
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)(void) = ^{
printf("aaaaa");
};
}
After compiling into cpp, the result is as follows
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
Equivalent to block
equals __ViewController__viewDidLoad_block_impl_0
, is a function.
Checking __ViewController__viewDidLoad_block_impl_0定义,是一个结构体,
can also show that block is a __ViewController__viewDidLoad_block_impl_0
type of object, which is why it block
can be %@
printed. block
The thing 本质
is 对象、函数、结构体
, since the block function has no name, it is also called匿名函数。
1.block是需要调用的
Function declaration: the internal implementation of the block is declared as a function __ViewController__viewDidLoad_block_func_0
Execute specific function implementation:FuncPtr
call the block to execute by calling the pointer of the block
2.block捕获外界变量
- (void)viewDidLoad {
[super viewDidLoad];
int t = 10;
void(^block)(void) = ^{
printf("aaaaa - %d",t);
};
block();
}
compile to 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);
}
It can be seen that when the block captures external variables, it will be in内部会自动生成同一个属性来保存
如果外界变量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
The structure will外界变量
be generated to save the original variable pointer and value, and the external variable can be operated inside the block (that is, pointer copy ).__Block_byref_a_0
The bottom real type of Block
Looking at the libclosure source code, we can see that the real type of the block is a structure .
// 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
};