Block類型
block有3種類型谴蔑,可以通過調(diào)用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]);
}
//打印結(jié)果
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__
//程序的內(nèi)存分配
image.png
- NSGlobalBlock:既沒有創(chuàng)建對象也沒有局部變量的時候凭豪,他是NSGlobalBlock類型,他存在內(nèi)存的數(shù)據(jù)區(qū)域
- NSMallocBlock:堆區(qū),動態(tài)分配內(nèi)存,alloc,堆區(qū)的特點要手動釋放晒杈,ARC環(huán)境下不用管
- NSStackBlock: 棧區(qū)嫂伞,放局部變量,棧的特點會自動分配內(nèi)存拯钻,離開作用域會自動銷毀
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]);
}
//打印結(jié)果
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
區(qū)分block類型
- 沒有訪問auto變量 就是NSGlobalBlock類型
- 訪問了auto變量 就是NSStackBlock
- NSStackBlock 調(diào)用了copy 就是NSMallocBlock
//下面代碼是在MRC情況下編譯的,因為ARC會自動調(diào)用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]);
}
//打印結(jié)果
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;
}
};
- 會發(fā)現(xiàn)所有的block的isa指向的類型都是_NSConcreteStackBlock類型
- 說明clang出來的代碼并不是我們最終OC寫的代碼,這只是編譯運行時的結(jié)果
接下來繼續(xù)上代碼
WKBlock myblock(){
WKBlock block = ^{
NSLog(@"------");
};
return block;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WKBlock block = myblock();
block();
}
return 0;
}
//打印結(jié)果
2018-08-16 19:12:40.518275+0800 mhhhh[16069:873326] ------
Program ended with exit code: 0
如果是MRC環(huán)境下
- 就有問題 block是存在于棧區(qū)(就會危險)
- 過了作用域就會釋放
ARC環(huán)境下會自動copy
WKBlock block = ^[{
NSLog(@"------");
} copy];
return block;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
WKBlock block = myblock();
block();
}
return 0;
}
堆和棧的區(qū)別
- 地址上來看 棧是高地址 堆是低地址
- 棧會自動釋放帖努,過了作用域立即釋放
- 堆需要手動釋放
- 棧吃了吐,堆吃了拉
__weak,__strong、__block粪般、__unsafe_unretained的區(qū)別
__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拼余、指定運行時系統(tǒng)版本,比如
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環(huán)境下 block會默認copy操作,也就是在堆上
WKPerson *__weak weakPerson; 外部傳的__weak 這里會生成__weak 對象
__main_block_desc_0 當有對象的時候 會多生成兩個函數(shù)
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); copy指向的是__main_block_copy_0 這個函數(shù)
void (*dispose)(struct __main_block_impl_0*); dispose 指向的是__main_block_dispose_0這個函數(shù)
- 當block copy的時候會調(diào)用
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);這個函數(shù)
void (copy)(struct __main_block_impl_0, struct __main_block_impl_0*);調(diào)用的時候內(nèi)部會執(zhí)行
內(nèi)部會調(diào)用 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對象進行引用
- 外面?zhèn)鞯氖莀_strong就強引用
- 外面?zhèn)鞯腳_weak就弱引用
如果剛剛是MRC會先釋放后打印,為啥因為block已經(jīng)釋放了
__block
首先來說修改block內(nèi)部對象的值
#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
我們轉(zhuǎn)換為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 會多一個結(jié)構(gòu)體__Block_byref_age_0
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
未完待續(xù)........