## Block拷貝
block的屬性需要使用copy修飾,如果block一旦沒有copy操作就不會在堆上,無法對block生命週期進行控制。
使用注意:循環引用的問題。
block在修改NSMutableArray,不需要添加__block,因爲修改內容也是對數組的使用,只有對對象賦值的時候才需要__block。
## __Block
當使用__block修飾符時,基本數據類型 a 被轉換成了__Block_byref_a_0結構體。__Block_byref_a_0結構體中帶有 isa指針,說明它也是一個對象。
當block修改變量時,會調用下面代碼:
__block修飾變量用結構體成員變量__forwarding可以實現無論 __block變量配置在棧上還是堆上都能夠正確的訪問 __block變量。
__forwarding指針始終指向自己。
當__block int a在棧中的時候,__forwarding指向的是棧中的自己。
當 i 拷貝到堆中時候,__forwarding指向的是堆中的自己。
## 鏈式編程
### ReactiveCocoa
### Masonry
### RxSwift
### SnapKit
## 類型
注意:在 ARC 開啓的情況下,將只會有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 類型的 block。因爲Block從棧拷貝到堆中。
### NSConcreteGlobalBlock
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __Block_byref_obj_1 {
void *__isa;
__Block_byref_obj_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
__Block_byref_obj_1 *obj = __cself->obj; // bound by ref
(a->__forwarding->a)++;
(obj->__forwarding->obj) = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
printf("截獲後--:%d", (a->__forwarding->a));
}
NSGlobalBlock是位於全局區的block,它是設置在程序的數據區域(.data區)中。
以下兩種情況下,block爲NSConcreteGlobalBlock
a.記述全局變量的地方有block語法時。
b.block語法的表達式中不使用任何外部變量時。
- 全局區block底層原理
struct __block_impl {
void *isa;//指向所屬類的指針,也就是block的類型
int Flags;//標誌變量,在實現block的內部操作時會用到
int Reserved;//保留變量
void *FuncPtr;//block執行時調用的函數指針
};
struct __globalBlock_block_impl_0 {
struct __block_impl impl;
struct __globalBlock_block_desc_0* Desc;
__globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __globalBlock_block_func_0(struct __globalBlock_block_impl_0 *__cself) {
static int g = 10;
printf("%d",g);
}
static struct __globalBlock_block_desc_0 {
size_t reserved;
size_t Block_size;
} __globalBlock_block_desc_0_DATA = { 0, sizeof(struct __globalBlock_block_impl_0)};
static __globalBlock_block_impl_0 __global_globalBlock_block_impl_0((void *)__globalBlock_block_func_0, &__globalBlock_block_desc_0_DATA);
void (*globalBlock)() = ((void (*)())&__global_globalBlock_block_impl_0);
int main(int argc, const char * argv[]) {
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
### NSConcreteStackBlock
NSStackBlock是位於棧區,超出變量作用域,棧上的Block以及 __block變量都被銷燬。
- 棧區block底層原理
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
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;//這個是C++的結構體 該函數是C++結構體的初始化函數
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
static struct __main_block_desc_0 {//這是對__main_block_impl_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[]) {
printf("Hello, World!\n");
int a = 10;
printf("%d", a);
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
### NSConcreteMallocBlock
NSMallocBlock是位於堆區,在變量作用域結束時不受影響。保存在堆中的 block,當引用計數爲 0 時會被銷燬。
堆中的block無法直接創建,其需要由_NSConcreteStackBlock類型的block拷貝而來(也就是說block需要執行copy之後才能存放到堆中)。由於block的拷貝最終都會調用_Block_copy_internal函數。
- 堆區block底層原理
__block int a = 10;
printf("截獲前:%d", a);
void (^block)(void) = [^{
a++;
printf("截獲後--:%d", a);
} copy];
a++;
printf("截獲後:%d", a);
block();
//這樣的話block的isa就是NSConcreteMallocBlock
typedef void (*blk_t)(void);
struct __getBlockArray0_block_impl_0 {
struct __block_impl impl;
struct __getBlockArray0_block_desc_0* Desc;
int val;
__getBlockArray0_block_impl_0(void *fp, struct __getBlockArray0_block_desc_0 *desc, int _val, int flags=0) : val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __getBlockArray0_block_func_0(struct __getBlockArray0_block_impl_0 *__cself) {
int val = __cself->val; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_0,val);}
static struct __getBlockArray0_block_desc_0 {
size_t reserved;
size_t Block_size;
} __getBlockArray0_block_desc_0_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_0)};
struct __getBlockArray0_block_impl_1 {
struct __block_impl impl;
struct __getBlockArray0_block_desc_1* Desc;
int val;
__getBlockArray0_block_impl_1(void *fp, struct __getBlockArray0_block_desc_1 *desc, int _val, int flags=0) : val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __getBlockArray0_block_func_1(struct __getBlockArray0_block_impl_1 *__cself) {
int val = __cself->val; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_1,val);}
static struct __getBlockArray0_block_desc_1 {
size_t reserved;
size_t Block_size;
} __getBlockArray0_block_desc_1_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_1)};
NSArray *getBlockArray0() {
int val =10;
return ((NSArray * _Nonnull (*)(id, SEL, ObjectType _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("arrayWithObjects:"), (id _Nonnull)((void (*)())&__getBlockArray0_block_impl_0((void *)__getBlockArray0_block_func_0, &__getBlockArray0_block_desc_0_DATA, val)), ((void (*)())&__getBlockArray0_block_impl_1((void *)__getBlockArray0_block_func_1, &__getBlockArray0_block_desc_1_DATA, val)), __null);
}
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a)++;
printf("截獲後--:%d", (a->__forwarding->a));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
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};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
printf("截獲前:%d", (a.__forwarding->a));
void (*block)(void) = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)), sel_registerName("copy"));
(a.__forwarding->a)++;
printf("截獲後:%d", (a.__forwarding->a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
## Block的本質
block本質上也是一個OC對象,它內部也有個isa指針
block是封裝了函數調用以及函數調用環境的OC對象
## 引用循環
產生引用循環的原因,是在堆上的對象與堆上的對象互相引用造成的環,就會引起循環引用。
在ARC下,只要將block賦值給一個變量,那麼這個block就將被拷貝到堆上,這是編譯器的優化。所以,這種情況下,block被強引用,block又對捕獲的對象強引用,我們就必須使用__weak來打破循環。但是有時候可能導致在block的過程被釋放了,我們就需要使用__strong對block外的__weak對象再次強引用,既能夠防止引用循環,又能夠保證代碼的正確執行。
可以傳入一個局部變量(即self),這樣就不會導致引用循環了。
可以__block解決(必要時調用block)。
## 截獲自動變量
這個其實也是block底層原理
利用__block
有三種情況不用__block也可以截獲變量的值:
靜態全局變量
全局變量
靜態變量
*XMind: ZEN - Trial Version*