Block其實就是一個代碼塊,一般被稱爲「閉包」,它封裝了函數調用以及函數調用環境,以便在合適的時機進行調用,在OC中,Block其實就是一個OC對象,它能夠當作參數傳遞。ios
Block的結構以下:面試
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^test)(void) = ^{
NSLog(@"Block");
};
test();
}
return 0;
}
複製代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
複製代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//Block的定義
void(*test)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA
);
//block的調用
test->FuncPtr(test);
}
return 0;
}
複製代碼
struct __main_block_impl_0 {
//存放了block的一些基本信息,包括isa,函數地址等等
struct __block_impl impl;
//存放block的一些描述信息
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;
}
};
複製代碼
因爲結構體__block_impl是直接存放在__main_block_impl_0結構體的內部,因此__main_block_impl_0結構體也能夠轉換成以下形式bash
struct __block_impl {
void *isa; //isa指針,能夠看出Block其實就是一個OC對象
int Flags; //標識,默認爲0
int Reserved; //保留字段
void *FuncPtr;//函數內存地址
};
struct __main_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0* Desc;
};
複製代碼
block將咱們所要調用的代碼封裝成了函數__main_block_func_0,而且將函數__main_block_func_0的內存地址保存在到void *FuncPtr中,具體函數以下閉包
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//此處就是調用的NSLog
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f3_lg91hwts5rjdlzjph0sn82m80000gp_T_main_4f0065_mi_0);
}
複製代碼
結構體__main_block_desc_0中則保存了block所佔用內存大小等描述信息iphone
static struct __main_block_desc_0 {
size_t reserved; //保留字段
size_t Block_size; //__main_block_impl_0結構體所佔內存大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
複製代碼
咱們在使用Block的過程當中,能夠在Block內部訪問外部的變量,包含局部變量、靜態變量(至關於私有的全局變量)、全局變量等等。如今就經過一個Demo來看一下block底層是如何訪問外部變量的。async
//定義全局變量c
int c = 30;
int main(int argc, const char * argv[]) {
@autoreleasepool {
//局部變量a
int a = 10;
//靜態變量b
static int b = 20;
void(^test)(void) = ^{
NSLog(@"Block - %d, %d, %d", a, b, c);
};
test();
}
return 0;
}
複製代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
static int b = 20;
void(*test)(void) = (&__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
a,
&b));
test->FuncPtr(test);
}
return 0;
}
複製代碼
能夠看出,此時__main_block_impl_0結構體中多了兩個參數,分別是局部變量a的值,靜態變量b的指針,也就是它的內存地址。函數
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
int *b;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
複製代碼
發現,在__main_block_impl_0結構體中多了兩個成員變量,一個是int a,一個是int *b學習
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
int *b = __cself->b; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f3_lg91hwts5rjdlzjph0sn82m80000gp_T_main_064cd6_mi_0, a, (*b), c);
}
複製代碼
在__main_block_func_0函數中,訪問局部變量a和靜態變量b時都是經過傳遞過來的__main_block_impl_0結構體拿到對應的成員變量進行訪問,可是全局變量c並無存放在結構體中,而是直接進行訪問。測試
此處須要注意的是,其實在OC中有個默認的關鍵字auto,在咱們建立局部變量的時候,會默認在局部變量前加上auto關鍵字進行修飾,例如上文中的int a,其實就至關於auto int a。auto關鍵字的含義就是它所修飾的變量會自動釋放,也表示着它所修飾的變量會存放到棧空間,系統會自動對其進行釋放。ui
block在編譯完成以後會轉換成結構體進行保存,結構體中的成員變量以下,其中在成員變量descriptor指向的結構體中,多了兩個函數指針分別爲copy和dispose,這兩個函數和block內部對象的內存管理有關,後面會具體說明。
block使用變量捕獲機制來保證在block內部可以正常的訪問外部變量。
在OC當中block其實擁有三種類型,能夠經過class或者isa指針來查看block具體的類型
int main(int argc, const char * argv[]) {
@autoreleasepool {
//第一種類型NSGlobalBlock
NSLog(@"%@",[^{
NSLog(@"NSGlobalBlock");
} class]);
//第二種類型NSStackBlock
int a = 10;
NSLog(@"%@",[^{
NSLog(@"%d", a);
} class]);
//第三種類型NSMallocBlock - 1
void(^test2)(void) = ^{
NSLog(@"NSMallocBlock - %d", a);
};
NSLog(@"%@",[test2 class]);
//第三種類型NSMallocBlock - 2
NSLog(@"%@",[[^{
NSLog(@"%d", a);
} copy] class]);
}
return 0;
}
複製代碼
運行結果以下:
圖中的block類型和上文中打印出來的block類型對應關係以下
class方法返回類型 | isa指向類型 |
---|---|
NSGlobalBlock | _NSConcreteGlobalBlock |
NSStackBlock | _NSConcreteStackBlock |
NSMallocBlock | _NSConcreteMallocBlock |
可是無論它是哪一種block類型,最終都是繼承自NSBlock類型,而NSBlock繼承自NSObject,因此這也說明了block自己就是一個對象。
在上述示例中,提到了四種生成不一樣類型的block的方法,分別以下:
其實第三點和第四點生成的都是NSMallocBlock,由此咱們就能夠獲得下面的結論
block的類型 | block執行的操做 |
---|---|
NSGlobalBlock | 沒有訪問auto類型的變量 |
NSStackBlock | 訪問了auto類型的變量 |
NSMallocBlock | __NSStackBlock__類型的block執行了copy操做 |
NSGlobalBlock、NSStackBlock和NSMallocBlock三種類型的block分別存放在了數據區、棧區和堆區。將三種類型的block分別進行copy操做以後,產生的結果以下:
結構圖以下
在上述示例中,NSStackBlock類型的block,執行了copy操做以後,生成的block爲NSMallocBlock,其實不止這一種方式生成NSMallocBlock,如下是OC中在ARC環境下自動觸發copy操做的幾種狀況:
typedef void(^block)(void);
block test(){
return ^{
NSLog(@"NSMallocBlock");
};
}
複製代碼
void(^test2)(void) = ^{
NSLog(@"NSMallocBlock - %d", a);
};
複製代碼
NSArray *arr = @[@"1",@"2"];
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"NSMallocBlock");
}];
複製代碼
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"NSMallocBlock");
});
複製代碼
咱們日常在使用block做爲屬性的時候,都會使用copy修飾符來修飾,其實內部就是對block進行了一次copy操做,將block拷貝到堆上,以便咱們手動管理block的內存
上文中Block訪問的外部變量都是基本數據類型,因此不涉及到內存管理,若是在block中訪問外部對象時,block內部又是什麼樣的結構呢?
int main(int argc, const char * argv[]) {
@autoreleasepool {
//默認對象
NSObject *obj1 = [[NSObject alloc] init];
void(^test1)(void) = ^{
NSLog(@"NSMallocBlock - %@", obj1);
};
test1();
//使用__weak指針修飾對象
NSObject *obj2 = [[NSObject alloc] init];
__weak typeof(obj2) weakObj = obj2;
void(^test2)(void) = ^{
NSLog(@"NSMallocBlock - %@", weakObj);
};
test2();
}
return 0;
}
複製代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
複製代碼
此處因爲使用了__weak關鍵字來修飾對象,涉及到runtime,全部須要指定runtime的版本。
//直接訪問外部對象的block內部結構
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//生成strong類型的指針
NSObject *__strong obj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _obj, int flags=0) : obj(_obj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//訪問__weak修飾符修飾的外部對象的block內部結構
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
//自動生成weak類型的指針
NSObject *__weak weakObj;
__main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, NSObject *__weak _weakObj, int flags=0) : weakObj(_weakObj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
複製代碼
這時發現,若是直接在block中訪問外部的auto類型的對象,默認是在block結構體中生成一個strong類型的指針指向外部對象,如結構體__main_block_impl_0。若是在block中訪問了__weak修飾符修飾的外部對象,那麼在它的內部會生成一個weak類型的指針指向外部對象,如結構體__main_block_impl_1。
在__main_block_impl_0的構造函數中,obj(_obj)就表明着,之後構造函數傳過來的_obj參數會自動賦值給結構體中的成員變量obj。
static struct __main_block_desc_0 {
size_t reserved; //保留字段
size_t Block_size; //整個block所佔內存空間
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); //copy函數
void (*dispose)(struct __main_block_impl_0*); //dispose函數
} __main_block_desc_0_DATA = { 0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0,
__main_block_dispose_0};
複製代碼
新增長了copy和dispose兩個函數指針,對應着函數__main_block_copy_0和__main_block_dispose_0,以下
//copy指針指向的函數
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
//dispose指針指向的函數
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
複製代碼
以前說過,block封裝了函數調用和函數調用環境,這也就意味這若是它引用了外部的對象,就須要對外部對象進行內存管理操做。__main_block_copy_0函數內部會調用_Block_object_assign函數,它的主要做用是根據外部引用的對象的修飾符來進行相應的操做,若是外部對象是使用__strong來修飾,那麼_Block_object_assign函數會對此對象進行一次相似retain的操做,使得外部對象的引用計數+1。
__main_block_dispose_0函數內部會調用_Block_object_dispose函數,它的做用就是在block內部函數執行完成以後對block內部引用的外部對象進行一次release操做。
若是block在棧上,那麼在block中訪問對象類型的auto變量時,是不會對auto變量產生強引用的。這個須要在MRC狀況下進行測試,將Xcode中Build Settings下的Automatic Reference Counting設置成NO,代表當前使用MRC環境。
@implementation XLPerson
- (void)dealloc{
[super dealloc];
NSLog(@"%s", __func__);
}
@end
複製代碼
typedef void(^TestBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
//建立block
TestBlock block;
{
XLPerson *person = [[XLPerson alloc] init];
block = ^{
NSLog(@"block --- %p", &person);
};
NSLog(@"%@", [block class]);
[person release];
}
NSLog(@"block執行前");
block();
[block release];
NSLog(@"block執行後");
}
return 0;
}
複製代碼
能夠發現,在MRC環境下,即便是有強指針指向block,系統也不會對block進行默認的copy操做,因此當前的block類型依舊爲NSStackBlock類型。並且,在block執行以前,XLPerson就已經釋放了,說明在棧上的block並無對person對象進行強引用。
函數 | 調用時機 |
---|---|
copy | 棧上的block被複制到堆上 |
dispose | 堆上的block被釋放時 |
使用block時,若是block中訪問到了外部被auto修飾的變量,咱們常用到__block來修飾外部變量,它的主要做用就是可以讓咱們在block內部來修改外部變量的值,固然,block只能用來修飾auto變量,不能用來修飾全局變量和靜態變量。
typedef void(^TestBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block XLPerson *person = [[XLPerson alloc] init];
__block int a = 10;
TestBlock block = ^{
person = nil;
a = 20;
NSLog(@"block -- a:%d, person:%@",a,person);
};
block();
NSLog(@"block調用後,a:%d, person:%@",a,person);
}
return 0;
}
複製代碼
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;
__Block_byref_person_0 *person; // by ref
__Block_byref_a_1 *a; // by ref
};
複製代碼
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//封裝person對象
__Block_byref_person_0 person = {
0, //
&person,
33554432,
sizeof(__Block_byref_person_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
objc_msgSend(objc_msgSend(objc_getClass("XLPerson"), sel_registerName("alloc")), sel_registerName("init"))};
//封裝變量a
__Block_byref_a_1 a = {
0,
(__Block_byref_a_1 *)&a,
0,
sizeof(__Block_byref_a_1),
10
};
//建立block
TestBlock block = (&__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
&person,
&a,
570425344));
block->FuncPtr(block);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f3_lg91hwts5rjdlzjph0sn82m80000gp_T_main_115560_mi_1,(a.__forwarding->a),(person.__forwarding->person));
}
return 0;
}
複製代碼
經過__block修飾的person對象在編譯後被封裝成了__Block_byref_person_0類型的結構體,內部有多個成員變量,以下
#將person對象封裝成結構體__Block_byref_person_0
struct __Block_byref_person_0 {
void *__isa; //isa指針
__Block_byref_person_0 *__forwarding; //forwarding指針
int __flags; //標識位
int __size; //結構體所佔內存大小
void (*__Block_byref_id_object_copy)(void*, void*);//函數指針指向copy函數
void (*__Block_byref_id_object_dispose)(void*); //函數指針指向dispose函數
XLPerson *__strong person; //強引用XLPerson的實例對象
};
//__Block_byref_person_0結構體的建立與賦值
__Block_byref_person_0 person = {
0, //對應isa指針,傳0
&person, //對應forwarding指針,將結構體自身的地址傳給了forwarding指針
33554432, //對應flags
sizeof(__Block_byref_person_0), //當前結構體所需內存大小
__Block_byref_id_object_copy_131, //copy函數
__Block_byref_id_object_dispose_131,//dispose函數
objc_msgSend(objc_msgSend(objc_getClass("XLPerson"),
sel_registerName("alloc")),
sel_registerName("init")) //經過objc_msgSend建立XLPerson對象,而且將對象的指針傳入結構體中
};
複製代碼
能夠明顯看出,在結構體__Block_byref_person_0中,存在以下成員變量
前文提到過,由於block封裝了函數調用環境,因此一旦它內部引用了外部的auto對象,就須要對外部對象的內存進行管理,因此纔有了copy函數和dispose函數。此處也同樣,由於使用__block修飾的XLPerson對象的指針存放在告終構體內部,因此須要使用copy函數和dispose函數來管理對象的內存。
若是使用__block來修飾基本數據類型的auto變量,就會將變量封裝成__Block_byref_a_1類型的結構體,內部結構以下
#將變量a封裝成結構體__Block_byref_a_1
struct __Block_byref_a_1 {
void *__isa; //isa指針
__Block_byref_a_1 *__forwarding;//forwarding指針
int __flags; //標識位
int __size; //結構體大小
int a; //變量a
};
//封裝變量a
__Block_byref_a_1 a = {
0, //isa,傳0
(__Block_byref_a_1 *)&a, //傳入當前結構體a的地址
0, //flags
sizeof(__Block_byref_a_1), //結構體的大小
10 //外部變量a的值
};
複製代碼
相對於__block修飾auto對象,若是修飾基本數據類型,則結構體中少了copy函數和dispose函數,由於基本數據類型不須要進行內存管理,因此不須要調用這兩個函數。
當block訪問外部__block修飾的auto變量時,會將變量封裝成結構體,而且將結構體的地址值存放在block內部
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_person_0 *person; // by ref
__Block_byref_a_1 *a; // by ref
};
複製代碼
其中person和a就是指向兩個__block結構體的指針,正由於在block中有引用到__Block_byref_person_0和__Block_byref_a_1,那麼block就必須對這兩個結構體的內存進行管理,因此相應的在__main_block_desc_0中就生成了兩個函數copy和dispose,專門用來管理person和a所指向的結構體(也是對象)的內存。以下
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*); //copy函數
void (*dispose)(struct __main_block_impl_0*); //dispose函數
} __main_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0, //copy函數
__main_block_dispose_0 //dispose函數
};
複製代碼
相應的copy函數和dispose函數以下
//copy函數
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign(&dst->person,src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign(&dst->a, src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
//dispose函數
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
複製代碼
這裏和上文中說到的block中訪問外部對象的內存管理相同
當圖中的Block0被賦值到堆上時,會將他所引用的__block變量一塊兒賦值到堆上,而且對堆上的__block變量產生強引用
當圖中的Block1被複制到堆上時,由於以前__block變量已經被複制到了堆上,因此Block1只是對堆上的__block變量產生強引用。
當Block0和Block1都被廢棄時,Block0和Block1對__block變量的引用會被釋放,因此__block變量最終由於沒有持有者而被廢棄
__block修飾的auto變量所對應的結構體以下
在結構體中有一個__forwarding指針指向本身,在後續訪問__block變量的時候也是經過__forwarding指針來進行訪問
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_1 *a = __cself->a; // bound by ref
__Block_byref_person_0 *person = __cself->person; // bound by ref
(a->__forwarding->a) = 20; //經過__forwarding指針來拿到a進行修改
(person->__forwarding->person) = __null; //經過__forwarding指針來拿到person進行修改
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f3_lg91hwts5rjdlzjph0sn82m80000gp_T_main_fbc4b7_mi_0,(a->__forwarding->a),(person->__forwarding->person));
}
複製代碼
當block在棧上時,__block變量也存放在棧上,它內部的__forwarding指針指向它自己
當block被複制到堆上以後,block所引用的__block變量也會被複制到堆上,這樣在棧上和堆上各存在一份__block變量,此時將棧上__block變量中的__forwarding指針指向堆上__block變量的地址,同時,堆上的__block變量中的__forwarding指針指向它自己,那麼此時,無論咱們是訪問棧上__block變量中的屬性值仍是堆上__block變量中的屬性值,都是經過__forwarding指針訪問到堆上的__block變量。
當block存放在棧上是,對對象類型的auto變量和__block變量都不會產生強引用
//copy函數
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
複製代碼
//copy函數
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign(&dst->a, src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
複製代碼
當block從堆中移除時,都會調用dispose函數來對引用的對象進行釋放
//dispose函數
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
複製代碼
//dispose函數
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
複製代碼
雖然調用的都是copy函數,可是傳遞的參數類型不一樣,訪問對象類型的auto變量時,傳遞的參數爲3(BLOCK_FIELD_IS_OBJECT),訪問__block變量時,傳遞的參數爲8(BLOCK_FIELD_IS_BYREF)。
在使用block時,若是block做爲一個對象的屬性,而且在block中也使用到了這個對象,則會產生循環引用,致使block和對象相互引用,沒法釋放。Demo以下
typedef void(^TestBlock)(void);
@interface XLPerson : NSObject
@property(nonatomic, copy)NSString *name;
@property(nonatomic, copy)TestBlock block;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
XLPerson *person = [[XLPerson alloc] init];
person.name = @"張三";
person.block = ^{
NSLog(@"%@",person.name);
};
person.block();
}
return 0;
}
複製代碼
解決方式有兩種(此處主要講解ARC的狀況下):
__weak typeof(person) weakPerson = person;
person.block = ^{
NSLog(@"%@",weakPerson.name);
};
複製代碼
__unsafe_unretained XLPerson *weakPerson = person;
person.block = ^{
NSLog(@"%@",weakPerson.name);
};
複製代碼
__weak和__unsafe_unretained最終的效果都是能shi使block不對外部訪問的對象造成強引用,而是造成弱引用。也就是說外部對象的引用計數不會增長。可是__weak和__unsafe_unretained也有區別,__weak在對象被銷燬後會自動將weak指針置爲nil,而__weak和__unsafe_unretained修飾的對象在被銷燬後,指針是不會被清空的,若是後續訪問到了這個指針,會報野指針的錯誤,所以在遇到循環引用的時候,優先使用__weak來解決。更多的關於__weak的內容會在後續文章中進行學習。
block其實就是封裝了函數調用與調用環境的OC對象,它的底層實際上是一個結構體。
在block中若是想要修改外部訪問的auto變量,就須要使用__block來修飾auto變量,它會將修飾的變量封裝成一個結構體,結構體內部存放着變量的值。若是__block修飾的是對象類型,那麼在結構體中會保存着存儲對象內存地址的指針,同時在結構體中還多出兩個函數指針copy和dispose,用來管理對象的內存。
在ARC中,block若是使用copy來修飾,會將block從棧上覆制到堆上,方便咱們手動管理block的內存,若是不用copy來修飾的話,那麼block就會存在棧上,由系統自動釋放內存。
在使用block過程當中,會遇到循環引用的問題,解決方式就是使用__weak或者__unsafa_unretain來修飾外部引用的對象。優先使用__weak。
以上內容純屬我的理解,若是有什麼不對的地方歡迎留言指正。
一塊兒學習,一塊兒進步~~~