狮子头镇楼编程
《Objective-C 高级编程》学习笔记1-内存管理
bash
参考资料:post
《Objective-C 高级编程》干货三部曲(二):Blocks篇ui
Blocks是C语言的扩充功能:带有局部变量的匿名函数;
spa
Blocks中将带有局部变量的匿名函数部分称为“Block literal”,或简称“Block”;
指针
“带有局部变量的匿名函数”这一律念并不只指Blocks,它还存在于其余程序语言中,在计算机科学中,此概念也称为必包。
code
Block语法:cdn
Block例子:
//一、可用 typedef 给Block定义别名
typedef int (^blk_t)(int);
int blockDemo() {
blk_t blk = ^(int count) {
return count + 1;
};
int a = blk(6);
printf("a = %d\n", a);
//二、Block会截获所使用的局部变量值,即保存该局部变量的瞬间值;
int b = 111;
//三、使用附有 __block 说明符的局部变量可在Block中赋值,该变量称为 __block 变量
__block int c = 222;
void (^blk_b)(void) = ^ {
printf("b1 = %d\n", b);
printf("c1 = %d\n", c);
c += b;
};
b = 222;
blk_b();
printf("b2 = %d\n", b);
printf("c2 = %d\n", c);
return 0;
}复制代码
输出结果是:
转换后能够在该文件夹获得一个:block.cpp,转换获得的C++代码就在其中。
OC代码:
int mainDemo() {
void (^blk)(void) = ^{
printf("Block\n");
};
blk();
return 0;
}复制代码
转换成C++后:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __mainDemo_block_impl_0 {
struct __block_impl impl;
struct __mainDemo_block_desc_0* Desc;
__mainDemo_block_impl_0(void *fp, struct __mainDemo_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//经过 Blocks 使用的匿名函数实际上被做为简单的C语言函数来处理
//参数 __cself 为指向 Block 值的变量
static void __mainDemo_block_func_0(struct __mainDemo_block_impl_0 *__cself) {
printf("Block\n");
}
static struct __mainDemo_block_desc_0 {
size_t reserved;
size_t Block_size;
} __mainDemo_block_desc_0_DATA = {
0,
sizeof(struct __mainDemo_block_impl_0)
};
int mainDemo() {
void (*blk)(void) = ((void (*)())&__mainDemo_block_impl_0((void *)__mainDemo_block_func_0,
&__mainDemo_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
// 去掉转换部分:
// struct __mainDemo_block_impl_0 tmp = __mainDemo_block_impl_0(__mainDemo_block_func_0, &__mainDemo_block_desc_0_DATA);
// struct __mainDemo_block_impl_0 *blk = &tmp;
return 0;
}复制代码
//“id”这一变量类型用于存储OC对象,它的声明以下:
typedef struct objc_object {
Class isa;
} *id;
//id 为 objc_objc 结构体的指针类型
//Class 为 objc_calss 结构体的指针类型
typedef struct objc_class *Class;
//objc_class 的声明以下:
struct objc_class {
Class isa;
};
//objc_object 结构体和 objc_class 结构体是在各个对象和类的实现中使用的最基本的结构体。复制代码
举个例子,定义一个OC类:MyObject
@interface MyObject : NSObject {
int val0;
int val1;
}
@end复制代码
基于 objc_object 结构体,MyObject类的对象结构体以下:
struct MyObject {
Class isa;//经过成员变量 isa 保持该类的结构体实例指针
int val0;
int val1;
}复制代码
那么MyObject的实质:
各种的结构体就是基于 objc_class 结构体的 class_t 结构体。class_t 结构体的声明以下:
struct class_t {
struct class_t *isa;
struct class_t *superclass;
Cache cache;
IMP *vtable;
uintptr_t data_NEVER_USE;
};
//该结构体实例会持有声明的成员变量、方法的名称、方法的实现(即函数指针)、属性以及父类的指针复制代码
//Block结构体
struct __mainDemo_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __mainDemo_block_desc_0* Desc;
};
// __mainDemo_block_impl_0 结构体至关于基于 objc_object 结构体的OC类对象的结构体。
// 对于其中成员变量 isa 进行初始化:
isa = &_NSConcreteStackBlock;
// 即 _NSConcreteStackBlock 至关于 class_t 结构体实例。
// 在将 Block 做为 OC 的对象处理时,关于该类的信息放置于 _NSConcreteStackBlock 中。
复制代码
“截获局部变量值”意味着在执行 Block 语法时,Block 语法表达式所使用的局部变量值被保存到 Block 的结构体实例中。
OC代码:
int mainDemo() {
int dmy = 256;
int val = 10;
const char *fmt = "var = %d\n";
void (^blk)(void) = ^{
printf(fmt, val);
};
val = 2;
fmt = "These values were changed. var = %d\n";
blk();
return 0;
}
复制代码
转换后C++代码核心部分:
struct __mainDemo_block_impl_0 {
struct __block_impl impl;
struct __mainDemo_block_desc_0* Desc;
//三、block中未使用的局部变量不会被截获
//四、block中使用的局部变量,截获的是自动变量值
const char *fmt;
int val;
};
static void __mainDemo_block_func_0(struct __mainDemo_block_impl_0 *__cself) {
// “截获局部变量值”意味着在执行 Block 语法时
// Block 语法表达式所使用的局部变量值被保存到 Block 的结构体实例中
const char *fmt = __cself->fmt;
int val = __cself->val;
printf(fmt, val);
}
int mainDemo() {
int dmy = 256;
int val = 10;
const char *fmt = "var = %d\n";
struct __mainDemo_block_impl_0 tmp = __mainDemo_block_impl_0(__mainDemo_block_func_0,
&__mainDemo_block_desc_0_DATA, fmt, val);
struct __mainDemo_block_impl_0 *blk = &tmp;
val = 2;
fmt = "These values were changed. var = %d\n";
blk->FuncPtr(blk);
return 0;
} 复制代码
C语言的函数中可能使用的变量:
其中,在函数的屡次调用之间可以传递值的变量有:
OC代码:
int global_val = 1;
static int static_global_val = 2;
int mainDemo() {
static int static_val = 3;
void (^blk)(void) = ^{
global_val *= 1;
static_global_val *= 2;
static_val *= 3;
};
blk();
return 0;
}复制代码
转换成C++后:
int global_val = 1;
static int static_global_val = 2;
struct __mainDemo_block_impl_0 {
struct __block_impl impl;
struct __mainDemo_block_desc_0* Desc;
int *static_val;
__mainDemo_block_impl_0(void *fp, struct __mainDemo_block_desc_0 *desc,
*_static_val, int flags=0) : static_val(_static_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block函数部分
static void __mainDemo_block_func_0(struct __mainDemo_block_impl_0 *__cself) {
int *static_val = __cself->static_val;
global_val *= 1;
static_global_val *= 3;
(*static_val) *= 3;
}
static struct __mainDemo_block_desc_0 {
size_t reserved;
size_t Block_size;
} __mainDemo_block_desc_0_DATA = {
0,
sizeof(struct __mainDemo_block_impl_0),
};
int mainDemo() {
static int static_val = 3;
blk = &__mainDemo_block_impl_0(__mainDemo_block_func_0,
&__mainDemo_block_desc_0_DATA, &static_val);
return 0;
}复制代码
OC代码:
int mainDemo() {
__block int block_val = 10;
void (^blk)(void) = ^{
block_val = 100;
};
blk();
return 0;
}
复制代码
转换成C++后:
// __block类型结构体
struct __Block_byref_block_val_0 {
void *__isa;
__Block_byref_block_val_0 *__forwarding;//指向本身的指针
int __flags;
int __size;
int block_val;//经过__forwarding进行访问
};
struct __mainDemo_block_impl_0 {
struct __block_impl impl;
struct __mainDemo_block_desc_0* Desc;
__Block_byref_block_val_0 *block_val;
__mainDemo_block_impl_0(void *fp, struct __mainDemo_block_desc_0 *desc,
__Block_byref_block_val_0 *_block_val, int flags=0)
: block_val(_block_val->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __mainDemo_block_func_0(struct __mainDemo_block_impl_0 *__cself) {
__Block_byref_block_val_0 *block_val = __cself->block_val;
(block_val->__forwarding->block_val) = 100;
}
static void __mainDemo_block_copy_0(struct __mainDemo_block_impl_0*dst, struct __mainDemo_block_impl_0*src) {
_Block_object_assign((void*)&dst->block_val, (void*)src->block_val, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __mainDemo_block_dispose_0(struct __mainDemo_block_impl_0*src) {
_Block_object_dispose((void*)src->block_val, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __mainDemo_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __mainDemo_block_impl_0*, struct __mainDemo_block_impl_0*);
void (*dispose)(struct __mainDemo_block_impl_0*);
} __mainDemo_block_desc_0_DATA = {
0,
sizeof(struct __mainDemo_block_impl_0),
__mainDemo_block_copy_0,
__mainDemo_block_dispose_0
};
int mainDemo() {
__Block_byref_block_val_0 block_val = {
0,
&block_val,
0,
__Block_byref_block_val_0,
10
};
blk = __mainDemo_block_impl_0(
__mainDemo_block_func_0, &__mainDemo_block_desc_0_DATA, &val, 0x22000000);
return 0;
}复制代码
将 Block 看成 OC对象 来看时,Block 有以下种类:
一、Block 什么时候为 _NSConcreteGlobalBlock 类对象:
二、设置在栈上的Block,若是其所属的变量做用域结束,该 Block 就被废弃,__block 变量也会被废弃:
三、 Blocks 提供了将 Block 和 __block 变量从栈上复制到堆上的方法来解决
四、各类 Block 调用 copy 方法的效果:
5、栈上 Block 复制到堆上的时机:
经过该功能,不管在 Block 语法位置,仍是 __block 变量配置在栈上或堆上,均可以顺利访问同一个 __block 变量。