獅子頭鎮樓編程
《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 變量。