iOS代碼塊Block
概述
代碼塊Block是蘋果在iOS4開始引入的對(duì)C語言的擴(kuò)展,用來實(shí)現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類型,其可以正常定義變量余佛、作為參數(shù)首妖、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時(shí)候調(diào)用,目前Block已經(jīng)廣泛應(yīng)用于iOS開發(fā)中,常用于GCD、動(dòng)畫、排序及各類回調(diào)
注: Block的聲明與賦值只是保存了一段代碼段,必須調(diào)用才能執(zhí)行內(nèi)部代碼
Block變量的聲明、賦值與調(diào)用
Block變量的聲明
Block內(nèi)訪問局部變量
在Block中可以訪問局部變量
// 聲明局部變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 100"
myBlock();
在聲明Block之后疆导、調(diào)用Block之前對(duì)局部變量進(jìn)行修改,在調(diào)用Block時(shí)局部變量值是修改之前的舊值
// 聲明局部變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺(tái)輸出"global = 100"
myBlock();
在Block中不可以直接修改局部變量
// 聲明局部變量global
int global = 100;
void(^myBlock)() = ^{
global ++; // 這句報(bào)錯(cuò)
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 100"
myBlock();
注: 原理解析,通過clang命令將OC轉(zhuǎn)為C++代碼來查看一下Block底層實(shí)現(xiàn),clang命令使用方式為終端使用cd定位到main.m文件所在文件夾,然后利用clang -rewrite-objc main.m將OC轉(zhuǎn)為C++,成功后在main.m同目錄下會(huì)生成一個(gè)main.cpp文件
// OC代碼如下
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 轉(zhuǎn)為C++代碼如下
void(*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, global));
// 將變量類型精簡(jiǎn)之后C++代碼如下,我們發(fā)現(xiàn)Block變量實(shí)際上就是一個(gè)指向結(jié)構(gòu)體__main_block_impl_0的指針,而結(jié)構(gòu)體的第三個(gè)元素是局部變量global的值
void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, global);
// 我們看一下結(jié)構(gòu)體__main_block_impl_0的代碼
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int global;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _global, int flags=0) : global(_global) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 在OC中調(diào)用Block的方法轉(zhuǎn)為C++代碼如下,實(shí)際上是指向結(jié)構(gòu)體的指針myBlock訪問其FuncPtr元素,在定義Block時(shí)為FuncPtr元素傳進(jìn)去的__main_block_func_0方法
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
// __main_block_func_0方法代碼如下,由此可見NSLog的global正是定義Block時(shí)為結(jié)構(gòu)體傳進(jìn)去的局部變量global的值
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int global = __cself->global; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_d5d9eb_mi_0, global);
}
// 由此可知,在Block定義時(shí)便是將局部變量的值傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對(duì)局部變量進(jìn)行修改并不會(huì)影響B(tài)lock內(nèi)部的值,同時(shí)內(nèi)部的值也是不可修改的
Block內(nèi)訪問__block修飾的局部變量
在局部變量前使用下劃線下劃線block修飾,在聲明Block之后、調(diào)用Block之前對(duì)局部變量進(jìn)行修改,在調(diào)用Block時(shí)局部變量值是修改之后的新值
// 聲明局部變量global
__block int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
在局部變量前使用下劃線下劃線block修飾,在Block中可以直接修改局部變量
// 聲明局部變量global
__block int global = 100;
void(^myBlock)() = ^{
global ++; // 這句正確
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
注: 原理解析,通過clang命令將OC轉(zhuǎn)為C++代碼來查看一下Block底層實(shí)現(xiàn)
// OC代碼如下
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 轉(zhuǎn)為C++代碼如下
void(*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_global_0 *)&global, 570425344));
// 將變量類型精簡(jiǎn)之后C++代碼如下,我們發(fā)現(xiàn)Block變量實(shí)際上就是一個(gè)指向結(jié)構(gòu)體__main_block_impl_0的指針,而結(jié)構(gòu)體的第三個(gè)元素是局部變量global的指針
void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &global, 570425344);
// 由此可知,在局部變量前使用__block修飾,在Block定義時(shí)便是將局部變量的指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對(duì)局部變量進(jìn)行修改會(huì)影響B(tài)lock內(nèi)部的值,同時(shí)內(nèi)部的值也是可以修改的
Block內(nèi)訪問全局變量
在Block中可以訪問全局變量
// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 100"
myBlock();
在聲明Block之后葛躏、調(diào)用Block之前對(duì)全局變量進(jìn)行修改,在調(diào)用Block時(shí)全局變量值是修改之后的新值
// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
在Block中可以直接修改全局變量
// 聲明全局變量global
int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
注: 原理解析,通過clang命令將OC轉(zhuǎn)為C++代碼來查看一下Block底層實(shí)現(xiàn)
// OC代碼如下
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 轉(zhuǎn)為C++代碼如下
void(*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// 將變量類型精簡(jiǎn)之后C++代碼如下,我們發(fā)現(xiàn)Block變量實(shí)際上就是一個(gè)指向結(jié)構(gòu)體__main_block_impl_0的指針,而結(jié)構(gòu)體中并未保存全局變量global的值或者指針
void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
// 我們看一下結(jié)構(gòu)體__main_block_impl_0的代碼
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;
}
};
// 在OC中調(diào)用Block的方法轉(zhuǎn)為C++代碼如下,實(shí)際上是指向結(jié)構(gòu)體的指針myBlock訪問其FuncPtr元素,在定義Block時(shí)為FuncPtr元素傳進(jìn)去的__main_block_func_0方法
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
// __main_block_func_0方法代碼如下,由此可見NSLog的global還是全局變量global的值
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_f35954_mi_0, global);
}
// 由此可知,全局變量所占用的內(nèi)存只有一份,供所有函數(shù)共同調(diào)用,在Block定義時(shí)并未將全局變量的值或者指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對(duì)局部變量進(jìn)行修改會(huì)影響B(tài)lock內(nèi)部的值,同時(shí)內(nèi)部的值也是可以修改的
Block內(nèi)訪問靜態(tài)變量
在Block中可以訪問靜態(tài)變量
// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 100"
myBlock();
在聲明Block之后澈段、調(diào)用Block之前對(duì)靜態(tài)變量進(jìn)行修改,在調(diào)用Block時(shí)靜態(tài)變量值是修改之后的新值
// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
global = 101;
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
在Block中可以直接修改靜態(tài)變量
// 聲明靜態(tài)變量global
static int global = 100;
void(^myBlock)() = ^{
global ++;
NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
注: 原理解析,通過clang命令將OC轉(zhuǎn)為C++代碼來查看一下Block底層實(shí)現(xiàn)
// OC代碼如下
void(^myBlock)() = ^{
NSLog(@"global = %d", global);
};
// 轉(zhuǎn)為C++代碼如下
void(*myBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &global));
// 將變量類型精簡(jiǎn)之后C++代碼如下,我們發(fā)現(xiàn)Block變量實(shí)際上就是一個(gè)指向結(jié)構(gòu)體__main_block_impl_0的指針,而結(jié)構(gòu)體的第三個(gè)元素是靜態(tài)變量global的指針
void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &global);
// 我們看一下結(jié)構(gòu)體__main_block_impl_0的代碼
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *global;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_global, int flags=0) : global(_global) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 在OC中調(diào)用Block的方法轉(zhuǎn)為C++代碼如下,實(shí)際上是指向結(jié)構(gòu)體的指針myBlock訪問其FuncPtr元素,在定義Block時(shí)為FuncPtr元素傳進(jìn)去的__main_block_func_0方法
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
// __main_block_func_0方法代碼如下,由此可見NSLog的global正是定義Block時(shí)為結(jié)構(gòu)體傳進(jìn)去的靜態(tài)變量global的指針
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *global = __cself->global; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_4d124d_mi_0, (*global));
}
// 由此可知,在Block定義時(shí)便是將靜態(tài)變量的指針傳給Block變量所指向的結(jié)構(gòu)體,因此在調(diào)用Block之前對(duì)靜態(tài)變量進(jìn)行修改會(huì)影響B(tài)lock內(nèi)部的值,同時(shí)內(nèi)部的值也是可以修改的
Block在MRC及ARC下的內(nèi)存管理
Block在MRC下的內(nèi)存管理
默認(rèn)情況下,Block的內(nèi)存存儲(chǔ)在棧中,不需要開發(fā)人員對(duì)其進(jìn)行內(nèi)存管理
// 當(dāng)Block變量出了作用域,Block的內(nèi)存會(huì)被自動(dòng)釋放
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
在Block的內(nèi)存存儲(chǔ)在棧中時(shí),如果在Block中引用了外面的對(duì)象,不會(huì)對(duì)所引用的對(duì)象進(jìn)行任何操作
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
[p release]; // Person對(duì)象在這里可以正常被釋放
如果對(duì)Block進(jìn)行一次copy操作,那么Block的內(nèi)存會(huì)被移動(dòng)到堆中,這時(shí)需要開發(fā)人員對(duì)其進(jìn)行release操作來管理內(nèi)存
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
Block_copy(myBlock);
// do something ...
Block_release(myBlock);
如果對(duì)Block進(jìn)行一次copy操作,那么Block的內(nèi)存會(huì)被移動(dòng)到堆中,在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對(duì)象,會(huì)對(duì)所引用的對(duì)象進(jìn)行一次retain操作,即使在Block自身調(diào)用了release操作之后,Block也不會(huì)對(duì)所引用的對(duì)象進(jìn)行一次release操作,這時(shí)會(huì)造成內(nèi)存泄漏
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
Block_copy(myBlock);
// do something ...
Block_release(myBlock);
[p release]; // Person對(duì)象在這里無法正常被釋放,因?yàn)槠湓贐lock中被進(jìn)行了一次retain操作
如果對(duì)Block進(jìn)行一次copy操作,那么Block的內(nèi)存會(huì)被移動(dòng)到堆中,在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對(duì)象,會(huì)對(duì)所引用的對(duì)象進(jìn)行一次retain操作,為了不對(duì)所引用的對(duì)象進(jìn)行一次retain操作,可以在對(duì)象的前面使用下劃線下劃線block來修飾
__block Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
Block_copy(myBlock);
// do something ...
Block_release(myBlock);
[p release]; // Person對(duì)象在這里可以正常被釋放
如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問了該對(duì)象,那么會(huì)造成循環(huán)引用
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
Person *p = [[Person alloc] init];
p.myBlock = ^{
NSLog(@"------%@", p);
};
p.myBlock();
[p release]; // 因?yàn)閙yBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會(huì)對(duì)Person對(duì)象進(jìn)行一次retain操作,導(dǎo)致循環(huán)引用無法釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
self.myBlock = ^{
NSLog(@"------%@", self);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
[p release]; // Person對(duì)象在這里無法正常釋放,雖然表面看起來一個(gè)alloc對(duì)應(yīng)一個(gè)release符合內(nèi)存管理規(guī)則,但是實(shí)際在resetBlock方法實(shí)現(xiàn)中,Block內(nèi)部對(duì)self進(jìn)行了一次retain操作,導(dǎo)致循環(huán)引用無法釋放
如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是在對(duì)象的前面使用下劃線下劃線block來修飾,以避免Block對(duì)對(duì)象進(jìn)行retain操作
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
__block Person *p = [[Person alloc] init];
p.myBlock = ^{
NSLog(@"------%@", p);
};
p.myBlock();
[p release]; // Person對(duì)象在這里可以正常被釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
// 這里為了通用一點(diǎn),可以使用__block typeof(self) p = self;
__block Person *p = self;
self.myBlock = ^{
NSLog(@"------%@", p);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
Block_release(_myBlock);
[super dealloc];
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
[p release]; // Person對(duì)象在這里可以正常被釋放
Block在ARC下的內(nèi)存管理
在ARC默認(rèn)情況下,Block的內(nèi)存存儲(chǔ)在堆中,ARC會(huì)自動(dòng)進(jìn)行內(nèi)存管理,程序員只需要避免循環(huán)引用即可
// 當(dāng)Block變量出了作用域,Block的內(nèi)存會(huì)被自動(dòng)釋放
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
在Block的內(nèi)存存儲(chǔ)在堆中時(shí),如果在Block中引用了外面的對(duì)象,會(huì)對(duì)所引用的對(duì)象進(jìn)行強(qiáng)引用,但是在Block被釋放時(shí)會(huì)自動(dòng)去掉對(duì)該對(duì)象的強(qiáng)引用,所以不會(huì)造成內(nèi)存泄漏
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
// Person對(duì)象在這里可以正常被釋放
如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問了該對(duì)象,那么會(huì)造成循環(huán)引用
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
p.myBlock = ^{
NSLog(@"------%@", p);
};
p.myBlock();
// 因?yàn)閙yBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統(tǒng)釋放),所以Block會(huì)對(duì)Person對(duì)象進(jìn)行一次強(qiáng)引用,導(dǎo)致循環(huán)引用無法釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
self.myBlock = ^{
NSLog(@"------%@", self);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
// Person對(duì)象在這里無法正常釋放,在resetBlock方法實(shí)現(xiàn)中,Block內(nèi)部對(duì)self進(jìn)行了一次強(qiáng)引用,導(dǎo)致循環(huán)引用無法釋放
如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是使用一個(gè)弱引用的指針指向該對(duì)象,然后在Block內(nèi)部使用該弱引用指針來進(jìn)行操作,這樣避免了Block對(duì)對(duì)象進(jìn)行強(qiáng)引用
情況一
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
__weak typeof(p) weakP = p;
p.myBlock = ^{
NSLog(@"------%@", weakP);
};
p.myBlock();
// Person對(duì)象在這里可以正常被釋放
情況二
@interface Person : NSObject
@property (nonatomic, copy) void(^myBlock)();
- (void)resetBlock;
@end
@implementation Person
- (void)resetBlock
{
// 這里為了通用一點(diǎn),可以使用__weak typeof(self) weakP = self;
__weak Person *weakP = self;
self.myBlock = ^{
NSLog(@"------%@", weakP);
};
}
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
Person *p = [[Person alloc] init];
[p resetBlock];
// Person對(duì)象在這里可以正常被釋放
Block在ARC下的內(nèi)存管理的官方案例
在MRC中,我們從當(dāng)前控制器采用模態(tài)視圖方式present進(jìn)入MyViewController控制器,在Block中會(huì)對(duì)myViewController進(jìn)行一次retain操作,造成循環(huán)引用
MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler =? ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
[myController release];
}];
在MRC中解決循環(huán)引用的辦法即在變量前使用下劃線下劃線block修飾,禁止Block對(duì)所引用的對(duì)象進(jìn)行retain操作
__block MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler =? ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
[myController release];
}];
但是上述方法在ARC下行不通,因?yàn)橄聞澗€下劃線block在ARC中并不能禁止Block對(duì)所引用的對(duì)象進(jìn)行強(qiáng)引用,解決辦法可以是在Block中將myController置空(為了可以修改myController,還是需要使用下劃線下劃線block對(duì)變量進(jìn)行修飾)
__block MyViewController *myController = [[MyViewController alloc] init];
// ...
myController.completionHandler =? ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};
[self presentViewController:myController animated:YES completion:^{}];
上述方法確實(shí)可以解決循環(huán)引用,但是在ARC中還有更優(yōu)雅的解決辦法,新創(chuàng)建一個(gè)弱指針來指向該對(duì)象,并將該弱指針放在Block中使用,這樣Block便不會(huì)造成循環(huán)引用
MyViewController *myController = [[MyViewController alloc] init];
// ...
__weak MyViewController *weakMyController = myController;
myController.completionHandler =? ^(NSInteger result) {
[weakMyController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{}];
雖然解決了循環(huán)引用,但是也容易涉及到另一個(gè)問題,因?yàn)锽lock是通過弱引用指向了myController對(duì)象,那么有可能在調(diào)用Block之前myController對(duì)象便已經(jīng)被釋放了,所以我們需要在Block內(nèi)部再定義一個(gè)強(qiáng)指針來指向myController對(duì)象
MyViewController *myController = [[MyViewController alloc] init];
// ...
__weak MyViewController *weakMyController = myController;
myController.completionHandler =? ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController)
{
[strongMyController dismissViewControllerAnimated:YES completion:nil];
}
else
{
// Probably nothing...
}
};
[self presentViewController:myController animated:YES completion:^{}];
這里需要補(bǔ)充一下,在Block內(nèi)部定義的變量,會(huì)在作用域結(jié)束時(shí)自動(dòng)釋放,Block對(duì)其并沒有強(qiáng)引用關(guān)系,且在ARC中只需要避免循環(huán)引用即可,如果只是Block單方面地對(duì)外部變量進(jìn)行強(qiáng)引用,并不會(huì)造成內(nèi)存泄漏
注: 關(guān)于下劃線下劃線block關(guān)鍵字在MRC和ARC下的不同
__block在MRC下有兩個(gè)作用
1. 允許在Block中訪問和修改局部變量
2. 禁止Block對(duì)所引用的對(duì)象進(jìn)行隱式retain操作
__block在ARC下只有一個(gè)作用
1. 允許在Block中訪問和修改局部變量
使用Block進(jìn)行排序
在開發(fā)中,我們一般使用數(shù)組的如下兩個(gè)方法來進(jìn)行排序
不可變數(shù)組的方法: - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr
可變數(shù)組的方法 : - (void)sortUsingComparator:(NSComparator)cmptr
其中,NSComparator是利用typedef定義的Block類型
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
其中,這個(gè)返回值為NSComparisonResult枚舉,這個(gè)返回值用來決定Block的兩個(gè)參數(shù)順序,我們只需在Block中指明不同條件下Block的兩個(gè)參數(shù)的順序即可,方法內(nèi)部會(huì)將數(shù)組中的元素分別利用Block來進(jìn)行比較并排序
typedef NS_ENUM(NSInteger, NSComparisonResult)
{
NSOrderedAscending = -1L, // 升序,表示左側(cè)的字符在右側(cè)的字符前邊
NSOrderedSame, // 相等
NSOrderedDescending // 降序,表示左側(cè)的字符在右側(cè)的字符后邊
};
我們以Person類為例,對(duì)Person對(duì)象以年齡升序進(jìn)行排序,具體方法如下
@interface Student : NSObject
@property (nonatomic, assign) int age;
@end
@implementation Student
@end
Student *stu1 = [[Student alloc] init];
stu1.age = 18;
Student *stu2 = [[Student alloc] init];
stu2.age = 28;
Student *stu3 = [[Student alloc] init];
stu3.age = 11;
NSArray *array = @[stu1,stu2,stu3];
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
Student *stu1 = obj1;
Student *stu2 = obj2;
if (stu1.age > stu2.age)
{
return NSOrderedDescending; // 在這里返回降序,說明在該種條件下,obj1排在obj2的后邊
}
else if (stu1.age < stu2.age)
{
return NSOrderedAscending;
}
else
{
return NSOrderedSame;
}
}];