Block分类
主要是根据内存管理进行分类
iOS中.m文件重写为C++文件
使用的命令行
clang 可以将OC源程序重写为.cpp的代码
clang -rewrite-objc main.m
一、Block根据内存分配分为三类
NSGlobalBlock 全局静态
NSMallocBlock 堆block
NSStackBlock 栈Block
二、Block无参数无返回值
Block : 保存一段代码块
持有 - 随时随地
匿名函数 --> block --> 对象
// 无参数无返回值
void(^globleBlock)(void) = ^{
NSLog(@"block调用");
};
globleBlock(); // block调用
NSLog(@"block = %@", globleBlock); //打印相应的类型
block = <__NSGlobalBlock__: 0x1035760a0>
三、Block无参数无返回值访问外部局部变量
分配在堆上的Block
// 无参数无返回值
int a = 10;
// block 特性:自动捕获变量
对象底层:struts
/// 增加属性
/// 栈 ---> 操作符重写 --> copy
void(^mallocBlock)(void) = ^{
NSLog(@"block调用访问外部变量:%d", a);
};
mallocBlock(); // block调用
/// 打印相应的类型
NSLog(@"block = %@", mallocBlock);
block = <__NSMallocBlock__: 0x600001643270>
四、不需要别名Block访问外部变量
分配在栈上的Block
int a = 10;
NSLog(@"block = %@", ^{
NSLog(@"block = %d", a); // block = <__NSStackBlock__: 0x7ffeed2379a8>
});
五、内存指针小常识
0x7: 是栈指针开头 例如:<NSStackBlock: 0x7ffeed2379a8>
0x6: 是堆指针开头 例如: <NSMallocBlock: 0x600001643270>
0x1: 全局静态指针开头 <NSGlobalBlock: 0x1035760a0>
六、NSLog本质 --print
I/O操作比较耗时
方法本质:消息
七、Block循环引用
typedef void(^KCBlock)(void);
@interface ViewController ()
@property(strong ,nonatomic)Person * p;
/// 定义一个Block
@property (nonatomic, copy) KCBlock block;
/// 定义一个字符串的
@property (nonatomic, copy) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"正在使用的BLock";
__weak typeof(self) weakSelf = self; // 导致循环引用使用__weak
self.block = ^{
NSLog(@"输出形影的:%@", weakSelf.name);
};
}
八、Block使用弱引用提前释放
- 界面已经消失,回调到才回来,导致无法执行后续方法
self.name = @"正在使用的BLock";
__weak typeof(self) weakSelf = self;
self.block = ^{
//NSLog(@"输出形影的:%@", weakSelf.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"当前打印:%@", self.name);// 获取的循环打印问题
});
};
- 解决提前释放的问题
需要延迟释放时候使用 __strong
__weak typeof(self) weakSelf = self;
__strong typeof(weakSelf) strongSelf = weakSelf;
self.name = @"正在使用的BLock";
__weak typeof(self) weakSelf = self;
__strong typeof(weakSelf) strongSelf = weakSelf;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"当前打印:%@", self.name);// 获取的循环打印问题
});
};
9、Block使用解决循环引用
1、
__block ViewController *vc = self;
解决循环引用,但会导致提前释放问题,没有走delloc函数
2、__block 会进行一次copy操作,对捕获的ViewController执行copy操作到当前的struct中被block持有
3.vc = nil
时候释放当前的引用,内存回收
1. __block解决循环引用
self.name = @"正在使用的BLock";
__block ViewController *vc = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"当前打印:%@",vc.name);// 获取的循环打印问题
});
};
2. 上面__block导致delloc没有执行解决问题
- 要在异步回调中将 __block ViewController *vc = self;
延迟执行的函数执行完毕释放掉 将vc = nil
当前的self持有block 当前block又持有当前的vc
当vc = nil 打破循环引用从而内存正常释放
self -> block --> vc --> self
__block ViewController *vc = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"当前打印:%@",vc.name);// 获取的循环打印问题
vc = nil
});
};
3. 解决循环引用可以直接使用传递当前的控制器解决
///
- (void)methedVCBlock {
self.name = @"传递VCBlock实现解决循环引用问题";
self.vcBlock = ^(ViewController *vc) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"当前打印:%@", vc.name);
});
};
}
十、使用__block 能够在block中捕获当前的变量
- 没有__block 是block进行捕获变量之后传递是值传递
- 有__block 是block进行捕获变量之后进行的是指针传递
// 指针的使用
- (void)fourMethed {
__block int a = 10; // 拷贝到堆区
NSLog(@"进去之前时候: %p", &a);
void(^myFirstBlock)(void) = ^{
a++;
NSLog(@"进去之后进行处理:%p", &a);
};
NSLog(@"执行完毕之后查看指针地址:%p", &a);
myFirstBlock();
}
十一、auto static 修饰的变量
- 自动变量可能会销毁,block在执行的时候可能自动变量已经被销毁,那么此时如果再去访问被销毁的地址肯定会发生坏内存访问,因此对于自动变量一定是值传递而不可能是指针传递了。
- 静态变量不会被销毁,所以完全可以传递地址。因为传递的是值的地址,所以在block调用之前修改地址中保存的值,block中的地址是不会变得。所以值会随之改变。
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int a = 10;
static int b = 11;
void(^block)(void) = ^{
NSLog(@"输出的结果是:, a = %d, b = %d", a,b);
};
a = 1;
b = 2;
block();
};
}
return 0;
}
从下面c++代码可以知道,传递a 是值传递 而b是指针传递
这是上面代码重写为C++代码
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a; // 传递的值
int *b; // 传递的是指针
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
十二、当变量为全局变量时候
int a = 10;
static int b = 11;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^block)(void) = ^{
NSLog(@"hello, a = %d, b = %d", a,b);
};
a = 1;
b = 2;
block();
}
return 0;
}
转换为C++代码如下
int a = 10;
static int b = 11;
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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s__l2kw7t3j7gv43m07ydztp7pc0000gn_T_main_7d7391_mi_0, a,b); // 打印数调用直接传递 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(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
a = 1;
b = 2;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
总结:
- 局部变量都会被block捕获
- 自动变量是值捕获
- 静态变量为地址捕获
- 全局变量则不会被block捕获
网友评论