Block类型
block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型,NSBlock继承与NSObject
- NSGlobalBlock ( _NSConcreteGlobalBlock )
- NSStackBlock ( _NSConcreteStackBlock )
- NSMallocBlock ( _NSConcreteMallocBlock )
先上代码 再来分析把
- (void)viewDidLoad {
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"--------");
};
int age = 10;
void (^block1)(void) = ^{
NSLog(@"%d",age);
};
block();
block1();
NSLog(@"block is: %@",block);
NSLog(@"block is: %@",block1);
NSLog(@"block is: %@",[^{
NSLog(@"%d",age);
} class]);
}
//打印结果
2018-08-16 18:46:12.503194+0800 Test[15733:832689] --------
2018-08-16 18:46:12.503402+0800 Test[15733:832689] 10
2018-08-16 18:46:12.503599+0800 Test[15733:832689] block is: <__NSGlobalBlock__: 0x103555090>
2018-08-16 18:46:12.503798+0800 Test[15733:832689] block is: <__NSMallocBlock__: 0x60000004b4c0>
2018-08-16 18:46:12.504141+0800 Test[15733:832689] block is: __NSStackBlock__
//程序的内存分配
- NSGlobalBlock:既没有创建对象也没有局部变量的时候,他是NSGlobalBlock类型,他存在内存的数据区域
- NSMallocBlock:堆区,动态分配内存,alloc,堆区的特点要手动释放,ARC环境下不用管
- NSStackBlock: 栈区,放局部变量,栈的特点会自动分配内存,离开作用域会自动销毁
block的父类
- (void)viewDidLoad {
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"--------");
};
block();
NSLog(@"%@",[block class]);
NSLog(@"%@",[[block class] superclass]);
NSLog(@"%@",[[[block class] superclass] superclass]);
NSLog(@"%@",[[[[block class] superclass] superclass] superclass]);
}
//打印结果
2018-08-16 18:55:37.953707+0800 Test[15816:846355] --------
2018-08-16 18:55:37.953938+0800 Test[15816:846355] __NSGlobalBlock__
2018-08-16 18:55:37.954237+0800 Test[15816:846355] __NSGlobalBlock
2018-08-16 18:55:37.954798+0800 Test[15816:846355] NSBlock
2018-08-16 18:55:37.955377+0800 Test[15816:846355] NSObject
- block的父类是NSBlock,NSBlock继承于NSObject
区分block类型
- 没有访问auto变量 就是NSGlobalBlock类型
- 访问了auto变量 就是NSStackBlock
- NSStackBlock 调用了copy 就是NSMallocBlock
//下面代码是在MRC情况下编译的,因为ARC会自动调用copy,后面会讲到
- (void)viewDidLoad {
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"--------");
};
int age = 10;
void (^block1)(void) = ^{
NSLog(@"%d",age);
};
void (^block2)(void) = [^{
NSLog(@"%d",age);
}copy];
block();
block1();
block2();
NSLog(@"block is: %@",[block class]);
NSLog(@"block is: %@",[block1 class]);
NSLog(@"block is: %@",[block2 class]);
}
//打印结果
2018-08-16 19:03:10.653042+0800 Test[15918:859044] block is: __NSGlobalBlock__
2018-08-16 19:03:10.653448+0800 Test[15918:859044] block is: __NSStackBlock__
2018-08-16 19:03:10.653609+0800 Test[15918:859044] block is: __NSMallocBlock__
进入c++文件看看里面是什么
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc viewContrller.m
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;
}
};
struct __ViewController__viewDidLoad_block_impl_1 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_1* Desc;
int age;
__ViewController__viewDidLoad_block_impl_1(void *fp, struct __ViewController__viewDidLoad_block_desc_1 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __ViewController__viewDidLoad_block_impl_2 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_2* Desc;
int age;
__ViewController__viewDidLoad_block_impl_2(void *fp, struct __ViewController__viewDidLoad_block_desc_2 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 会发现所有的block的isa指向的类型都是_NSConcreteStackBlock类型
- 说明clang出来的代码并不是我们最终OC写的代码,这只是编译运行时的结果
接下来继续上代码
WKBlock myblock(){
WKBlock block = ^{
NSLog(@"------");
};
return block;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WKBlock block = myblock();
block();
}
return 0;
}
//打印结果
2018-08-16 19:12:40.518275+0800 mhhhh[16069:873326] ------
Program ended with exit code: 0
如果是MRC环境下
- 就有问题 block是存在于栈区(就会危险)
- 过了作用域就会释放
ARC环境下会自动copy
WKBlock block = ^[{
NSLog(@"------");
} copy];
return block;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WKBlock block = myblock();
block();
}
return 0;
}
堆和栈的区别
- 地址上来看 栈是高地址 堆是低地址
- 栈会自动释放,过了作用域立即释放
- 堆需要手动释放
- 栈吃了吐,堆吃了拉
__weak,__strong、__block、__unsafe_unretained的区别
__weak, __strong
#import <Foundation/Foundation.h>
#import "WKPerson.h"
typedef void (^WKBlock)(void);
int main(int argc, const char * argv[]) {
WKBlock block;
{
WKPerson *person = [[WKPerson alloc] init];
person.age = 10;
__weak WKPerson *weakPerson = person;
block = ^{
NSLog(@"---------%d", weakPerson.age);
};
NSLog(@"------");
}
return 0;
}
#import <Foundation/Foundation.h>
@interface WKPerson : NSObject
@property (assign, nonatomic) int age;
@end
#import "WKPerson.h"
@implementation WKPerson
- (void)dealloc
{
NSLog(@"WKPerson - dealloc");
}
@end
2018-08-17 18:14:43.170714+0800 cestess[14399:659983] ------
2018-08-17 18:14:43.170964+0800 cestess[14399:659983] WKPerson - dealloc
Program ended with exit code: 0
我们打印C++文件看看
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
//报错了 说不能编译__weak
error:
cannot create __weak reference because the current deployment target does
not support weak references
__attribute__((objc_ownership(weak))) WKPerson *weakPerson = person;
//解决方案 换成下面的指令 支持ARC、指定运行时系统版本,比如
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
WKPerson *__weak weakPerson;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, WKPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
WKPerson *__weak weakPerson = __cself->weakPerson; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_v2_sw9667rd2fn8s8hvf6pqn3ph0000gn_T_main_3620fe_mi_0, ((int (*)(id, SEL))(void *)objc_msgSend)((id)weakPerson, 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->weakPerson, (void*)src->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->weakPerson, 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};
我来解析一下这段代码
因为是ARC环境下 block会默认copy操作,也就是在堆上
WKPerson *__weak weakPerson; 外部传的__weak 这里会生成__weak 对象
__main_block_desc_0 当有对象的时候 会多生成两个函数
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); copy指向的是__main_block_copy_0 这个函数
void (*dispose)(struct __main_block_impl_0*); dispose 指向的是__main_block_dispose_0这个函数
- 当block copy的时候会调用
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);这个函数
void (copy)(struct __main_block_impl_0, struct __main_block_impl_0*);调用的时候内部会执行
内部会调用 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, 3/*BLOCK_FIELD_IS_OBJECT*/);}
_Block_object_assign 会对person对象进行引用
- 外面传的是__strong就强引用
- 外面传的__weak就弱引用
如果刚刚是MRC会先释放后打印,为啥因为block已经释放了
__block
首先来说修改block内部对象的值
#import <Foundation/Foundation.h>
typedef void (^WKBlock)(void);
int main(int argc, const char * argv[]) {
__block int age = 10;
// static int age = 10; 这样也可以修改 上篇文章讲到了 我就不讲了
WKBlock block = ^{
age = 20;
NSLog(@"%d",age);
};
block();
return 0;
}
2018-08-17 18:46:02.927284+0800 cestess[14723:713812] 20
Program ended with exit code: 0
我们转换为c++看看
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;
}
};
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int 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_v2_sw9667rd2fn8s8hvf6pqn3ph0000gn_T_main_4fe34f_mi_0,(age->__forwarding->age));
}
int main(int argc, const char * argv[]) {
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
WKBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
我来解析一下这段代码
- 使用了__block 会多一个结构体__Block_byref_age_0
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
未完待续........