原做於:2018-01-02
GitHub Repo:BoyangBloghtml
block是C語言的擴充功能,咱們能夠認爲它是 帶有自動變量的匿名函數。c++
block是一個匿名的inline代碼集合:git
- 參數列表,就像一個函數。
- 是一個對象!
- 有聲明的返回類型
- 可得到義詞法範圍的狀態,。
- 可選擇性修改詞法範圍的狀態。
- 能夠用相同的詞法範圍內定義的其它block共享進行修改的可能性
- 在詞法範圍(堆棧框架)被破壞後,能夠繼續共享和修改詞法範圍(堆棧框架)中定義的狀態
最簡單。github
int (^DefaultBlock1)(int) = ^int (int a) { return a + 1; }; DefaultBlock1(1); 複製代碼
升級版。macos
// 利用 typedef 聲明block typedef return_type (^BlockTypeName)(var_type); // 做屬性 @property (nonatomic, copy ,nullable) BlockTypeName blockName; // 做方法參數 - (void)requestForSomething:(Model)model handle:(BlockTypeName)handle; 複製代碼
在LLVM的文件中,我找到了一份文檔,Block_private.h,這裏能夠查看到block的實現狀況數組
struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */ }; struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; 複製代碼
裏面的invoke就是指向具體實現的函數指針,當block被調用的時候,程序最終會跳轉到這個函數指針指向的代碼區。 而 Block_descriptor裏面最重要的就是 copy函數和 dispose函數,從命名上能夠推斷出,copy函數是用來捕獲變量並持有引用,而dispose函數是用來釋放捕獲的變量。函數捕獲的變量會存儲在結構體 Block_layout的後面,在invoke函數執行前所有讀出。安全
按照慣例,使用 clang -rewrite-objc 將一個代碼進行編譯轉換,將獲得一份C++代碼。刨除其餘無用的代碼:markdown
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; 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 { 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[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA); } return 0; } 複製代碼
先看最直接的 __block_impl代碼,app
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; 複製代碼
這裏是一個結構體,裏面的元素分別是框架
- isa,指向所屬類的指針,也就是block的類型
- flags,標誌變量,在實現block的內部操做時會用到
- Reserved,保留變量
- FuncPtr,block執行時調用的函數指針
接着, __main_block_impl_0由於包含了__block_impl,咱們能夠將它打開,直接當作
__main_block_impl_0{ void *isa; int Flags; int Reserved; void *FuncPtr; struct __main_block_desc_0 *Desc; } 複製代碼
這麼一來,咱們能夠將block理解爲,一個OC對象、一個函數。
咱們能夠知道,咱們常見的block是有三種:
- __NSGlobalBlock
- __NSStackBlock
- __NSMallocBlock
void (^block)(void) = ^{ NSLog(@"biboyang"); }; block(); 複製代碼
或
static int age = 10; void(^block)(void) = ^{ NSLog(@"Hello, World! %d",age); }; block(); 複製代碼
像是這種,沒有對外捕獲變量的,就是GlobaBlock。
int b = 10; void(^block2)(void) = ^{ NSLog(@"Hello, World! %d",b); }; block2(); 複製代碼
這種block,在MRC中,便是StackBlock。在ARC中,由於編譯器作了優化,自動進行了copy,這種就是MallocBlock了。
之因此作這種優化的緣由很好理解:
若是StackBlock訪問了一個auto變量,由於本身是存在Stack上的,因此變量也就會被保存在棧上。可是由於棧上的數據是由系統自動進行管理的,隨時都有可能被回收。很是容易形成野指針的問題。
怎麼解決呢?複製到堆上就行了!
ARC也是如此作的。它會自動將棧上的block複製到堆上,因此,ARC下的block的屬性關鍵詞其實使用strong和copy都不會有問題,不過爲了習慣,仍是使用copy爲好。
Blcok的類 | 副本源的配置存儲域 | 複製效果 |
---|---|---|
__NSStackBlock | 棧 | 堆 |
__NSGlobalBlock | 程序的數據區域 | 無用 |
__NSMallocBlock | 堆 | 引用計數增長 |
系統默認調用copy方法把Block賦複製的四種狀況
- 手動調用copy
- Block是函數的返回值
- Block被強引用,Block被賦值給__strong或者id類型
- 調用系統API入參中含有usingBlcok的Cocoa方法或者GCD的相關API
ARC環境下,一旦Block賦值就會觸發copy,__block就會copy到堆上,Block也是__NSMallocBlock。ARC環境下也是存在__NSStackBlock的時候,這種狀況下,__block就在棧上。
這裏直接拿冰霜的文章來用
#import <Foundation/Foundation.h> int global_i = 1; static int static_global_j = 2; int main(int argc, const char * argv[]) { static int static_k = 3; int val = 4; void (^myBlock)(void) = ^{ global_i ++; static_global_j ++; static_k ++; NSLog(@"Block中 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val); }; global_i ++; static_global_j ++; static_k ++; val ++; NSLog(@"Block外 global_i = %d,static_global_j = %d,static_k = %d,val = %d",global_i,static_global_j,static_k,val); myBlock(); return 0; } 複製代碼
運行結果
Block 外 global_i = 2,static_global_j = 3,static_k = 4,val = 5 Block 中 global_i = 3,static_global_j = 4,static_k = 5,val = 4 複製代碼
轉換的結果爲
int global_i = 1; static int static_global_j = 2; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *static_k; int val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_k, int _val, int flags=0) : static_k(_static_k), val(_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *static_k = __cself->static_k; // bound by copy int val = __cself->val; // bound by copy global_i ++; static_global_j ++; (*static_k) ++; NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_0,global_i,static_global_j,(*static_k),val); } static struct __main_block_desc_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[]) { static int static_k = 3; int val = 4; void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val)); global_i ++; static_global_j ++; static_k ++; val ++; NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_6fe658_mi_1,global_i,static_global_j,static_k,val); ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock); return 0; } 複製代碼
首先全局變量global_i和靜態全局變量static_global_j的值增長,以及它們被Block捕獲進去,這一點很好理解,由於是全局的,做用域很廣,因此Block捕獲了它們進去以後,在Block裏面進行++操做,Block結束以後,它們的值依舊能夠得以保存下來。
在__main_block_impl_0中,能夠看到靜態變量static_k和自動變量val,被Block從外面捕獲進來,成爲__main_block_impl_0這個結構體的成員變量了。 在執行Block語法的時候,Block語法表達式所使用的自動變量的值是被保存進了Block的結構體實例中,也就是Block自身中。
這麼來就清晰了不少,自動變量是以值傳遞方式傳遞到Block的構造函數裏面去的。Block只捕獲Block中會用到的變量。因爲只捕獲了自動變量的值,並不是內存地址,因此Block內部不能改變自動變量的值。
截獲變量並修改有兩種方法 __block 和 指針法(不過__block法歸根結底,其實也是操做指針)。 這裏描述一下指針法:
NSMutableString * str = [[NSMutableString alloc]initWithString:@"Hello,"]; void (^myBlock)(void) = ^{ [str appendString:@"World!"]; NSLog(@"Block中 str = %@",str); }; NSLog(@"Block外 str = %@",str); myBlock(); const char *text = "hello"; void(^block)(void) = ^{ printf("%caaaaaaaaaaa\n",text[2]); }; block(); 複製代碼
直接操做指針去進行截獲,不過通常來說,這種方法多用於C語言數組的時候。使用OC的時候多數是使用__block。
這裏寫一個__block的捕獲代碼,使用剛纔的方法再來一次:
struct __Block_byref_i_0 { void *__isa; __Block_byref_i_0 *__forwarding;//指向真正的block int __flags; int __size; int i;//對象 }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_i_0 *i; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__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_i_0 *i = __cself->i; // bound by ref (i->__forwarding->i) ++; NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_3b0837_mi_0,(i->__forwarding->i)); } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 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, const char * argv[]) { __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0}; void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344)); ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock); return 0; } 複製代碼
咱們能夠發現這裏多了兩個結構體
struct __Block_byref_i_0 { void *__isa; __Block_byref_i_0 *__forwarding; int __flags; int __size; int i; }; 複製代碼
這個實例內,包含了 __isa 指針、一個標誌位 __flags 、一個記錄大小的 __size 。最最重要的,多了一個 __forwarding 指針和 val 變量. 這裏長話短說,出來了一個新的 __forwarding指針,指向告終構體實例自己在內存的地址。
block經過指針的持續傳遞,將使用的自動變量值保存到了block的結構體實例中。在block體內修改 __block0變量,經過一系列指針指向關係,最終指向了__Block_byref_age_0結構體內與局部變量同名同類型的那個成員,併成功修改變量值。
在棧中, __forwarding指向了本身自己,可是若是複製到了堆上,__forwarding就指向複製到堆上的block,而堆上的block中的 __forwarding這時候指向了本身。
//如下代碼是在ARC下執行的 #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { __block id block_obj = [[NSObject alloc]init]; id obj = [[NSObject alloc]init]; NSLog(@"block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj); void (^myBlock)(void) = ^{ NSLog(@"***Block中****block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj); }; myBlock(); return 0; } 複製代碼
轉換以後
struct __Block_byref_block_obj_0 { void *__isa; __Block_byref_block_obj_0 *__forwarding;//指向真正的block int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); id block_obj; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; id obj; __Block_byref_block_obj_0 *block_obj; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__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_block_obj_0 *block_obj = __cself->block_obj; // bound by ref id obj = __cself->obj; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_e64910_mi_1,(block_obj->__forwarding->block_obj) , &(block_obj->__forwarding->block_obj) , obj , &obj); } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);} static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj, 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}; int main(int argc, const char * argv[]) { __attribute__((__blocks__(byref))) __Block_byref_block_obj_0 block_obj = {(void*)0,(__Block_byref_block_obj_0 *)&block_obj, 33554432, sizeof(__Block_byref_block_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))}; id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")); NSLog((NSString *)&__NSConstantStringImpl__var_folders_45_k1d9q7c52vz50wz1683_hk9r0000gn_T_main_e64910_mi_0,(block_obj.__forwarding->block_obj) , &(block_obj.__forwarding->block_obj) , obj , &obj); void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, (__Block_byref_block_obj_0 *)&block_obj, 570425344)); ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock); return 0; } 複製代碼
在轉換出來的源碼中,咱們也能夠看到,Block捕獲了__block,而且強引用了,由於在__Block_byref_block_obj_0結構體中,有一個變量是id block_obj,這個默認也是帶__strong全部權修飾符的。
根據打印出來的結果來看,ARC環境下,Block捕獲外部對象變量,是都會copy一份的,地址都不一樣。只不過帶有__block修飾符的變量會被捕獲到Block內部持有。
在ARC中,對於聲明爲__block的外部對象,在block內部會進行retain,以致於在block環境內能安全的引用外部對象。
以前一直沒有想到過一個問題:
咱們知道不該該在block中使用實例變量,是由於會發生循環引用;那爲何會發生循環引用呢? 通常咱們會理解爲,一個_age的實例變量,其實是self->_age。那麼若是往下深究下去呢?
受談談ivar的直接訪問的啓發,我也開始探索一下這裏的緣由。
寫以下的代碼:
#import <Foundation/Foundation.h> #import "objc/runtime.h" typedef void(^MyBlock)(void); @interface MyObject : NSObject @property (nonatomic) NSUInteger BRInteger; @property (nonatomic, copy) NSString *BRString; @property (nonatomic, copy) MyBlock BRBlock; - (void)inits; @end @implementation MyObject - (void)inits { self.BRBlock = ^{ _BRInteger = 5; _BRString = @"Balaeniceps_rex"; }; } @end int main(int argc, const char * argv[]) { @autoreleasepool { MyObject *object = [MyObject new]; [object inits]; } return 0; } 複製代碼
使用 clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations main.m命令進行轉換。獲得如下的代碼(爲了簡便,將代碼作了省略):
typedef void(*MyBlock)(void);
#ifndef _REWRITER_typedef_MyObject
#define _REWRITER_typedef_MyObject
typedef struct objc_object MyObject;
typedef struct {} _objc_exc_MyObject;
#endif
//對於每一個ivar,都有對應的全局變量
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRInteger;
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRString;
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRBlock;
//內部的結構
struct MyObject_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSUInteger _BRInteger;
NSString *__strong _BRString;
__strong MyBlock _BRBlock;
};
// @property (nonatomic) NSUInteger BRInteger;
// @property (nonatomic, copy) NSString *BRString;
// @property (nonatomic, copy) MyBlock BRBlock;
// - (void)inits;
/* @end */
// @implementation MyObject
struct __MyObject__inits_block_impl_0 {
struct __block_impl impl;
struct __MyObject__inits_block_desc_0* Desc;
MyObject *const __strong self;
//注意這裏捕捉了self
__MyObject__inits_block_impl_0(void *fp, struct __MyObject__inits_block_desc_0 *desc, MyObject *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block的函數方法(也就是方法layout中第四行的那個)
static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) {
MyObject *const __strong self = __cself->self; // bound by copy
//這裏是經過self的地址,那倒全局變量的偏移去獲取實例變量的地址
(*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)) = 5;
(*(NSString *__strong *)((char *)self + OBJC_IVAR_$_MyObject$_BRString)) = (NSString *)&__NSConstantStringImpl__var_folders_m1_05zb_zbd1g1f8k27nc6yn_th0000gn_T_main_e9db32_mi_0;
}
static void __MyObject__inits_block_copy_0(struct __MyObject__inits_block_impl_0*dst, struct __MyObject__inits_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __MyObject__inits_block_dispose_0(struct __MyObject__inits_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __MyObject__inits_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __MyObject__inits_block_impl_0*, struct __MyObject__inits_block_impl_0*);
void (*dispose)(struct __MyObject__inits_block_impl_0*);
} __MyObject__inits_block_desc_0_DATA = { 0, sizeof(struct __MyObject__inits_block_impl_0), __MyObject__inits_block_copy_0, __MyObject__inits_block_dispose_0};
static void _I_MyObject_inits(MyObject * self, SEL _cmd) {
((void (*)(id, SEL, MyBlock))(void *)objc_msgSend)((id)self, sel_registerName("setBRBlock:"), ((void (*)())&__MyObject__inits_block_impl_0((void *)__MyObject__inits_block_func_0, &__MyObject__inits_block_desc_0_DATA, self, 570425344)));
}
static NSUInteger _I_MyObject_BRInteger(MyObject * self, SEL _cmd) { return (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)); }
static void _I_MyObject_setBRInteger_(MyObject * self, SEL _cmd, NSUInteger BRInteger) { (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)) = BRInteger; }
static NSString * _I_MyObject_BRString(MyObject * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_MyObject$_BRString)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_MyObject_setBRString_(MyObject * self, SEL _cmd, NSString *BRString) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct MyObject, _BRString), (id)BRString, 0, 1); }
static void(* _I_MyObject_BRBlock(MyObject * self, SEL _cmd) )(){ return (*(__strong MyBlock *)((char *)self + OBJC_IVAR_$_MyObject$_BRBlock)); }
static void _I_MyObject_setBRBlock_(MyObject * self, SEL _cmd, MyBlock BRBlock) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct MyObject, _BRBlock), (id)BRBlock, 0, 1); }
// @end
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
MyObject *object = ((MyObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MyObject"), sel_registerName("new"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)object, sel_registerName("inits"));
}
return 0;
}
複製代碼
咱們能夠發現,每一個實例變量都是被建立了對應的全局變量:
extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRInteger; extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRString; extern "C" unsigned long OBJC_IVAR_$_MyObject$_BRBlock; 複製代碼
下面是block的layout中的第四排的函數調用方法。
//block的函數方法(也就是方法layout中第四行的那個) static void __MyObject__inits_block_func_0(struct __MyObject__inits_block_impl_0 *__cself) { MyObject *const __strong self = __cself->self; // bound by copy //這裏是經過self的地址,那倒全局變量的偏移去獲取實例變量的地址 (*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObject$_BRInteger)) = 5; (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_MyObject$_BRString)) = (NSString *)&__NSConstantStringImpl__var_folders_m1_05zb_zbd1g1f8k27nc6yn_th0000gn_T_main_e9db32_mi_0; } 複製代碼
經過這裏,咱們其實也能發現,這裏是經過self的偏移去獲取實例變量的地址,也是和self息息相關的。
若是這個還不會證實實例變量中的self的做用的話,咱們接着往下看;
struct __MyObject__inits_block_impl_0 { struct __block_impl impl; struct __MyObject__inits_block_desc_0* Desc; MyObject *const __strong self; //注意這裏捕捉了self __MyObject__inits_block_impl_0(void *fp, struct __MyObject__inits_block_desc_0 *desc, MyObject *const __strong _self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; 複製代碼
在這個方法裏,咱們能夠發現,在block當中,其實也引用到MyObject,是一個強引用的self!而block的構造函數中也屢次引用了self。
咱們若是瞭解過property的話,也會知道實例變量是在編譯期就肯定地址了。內部實現的全局變量就表明了地址的offset。
若是咱們把block設置爲nil,而後去調用,會發生什麼?
void (^block)(void) = nil; block(); 複製代碼
當咱們運行的時候,它會崩潰,報錯信息爲 Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)。
咱們能夠發現,當把block置爲nil的時候,第四行的函數指針,被置爲NULL,注意,這裏是NULL而不是nil。咱們給一個對象發送nil消息是沒有問題的,可是給若是是NULL就會發生崩潰。
它直接訪問到了函數指針,由於前三位分別是void、int、int,大小分別是八、四、4,加一塊就爲16,因此在十六位中,就表示出0x10地址的崩潰。 若是是在32位的系統中,void的大小是4,崩潰的地址應該就是0x0c。
下一篇文章block(二):block的copy
Blocks Programming Topics
Working with Blocks
fuckingblocksyntax.com