iOS代碼塊Block理解

概述

參考文章:http://www.reibang.com/p/14efa33b3562

代碼塊Block是蘋(píng)果在iOS4開(kāi)始引入的對(duì)C語(yǔ)言的擴(kuò)展,用來(lái)實(shí)現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類(lèi)型,其可以正常定義變量寄雀、作為參數(shù)贷祈、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時(shí)候調(diào)用,目前Block已經(jīng)廣泛應(yīng)用于iOS開(kāi)發(fā)中,常用于GCD健民、動(dòng)畫(huà)、排序及各類(lèi)回調(diào)

注: Block的聲明與賦值只是保存了一段代碼段,必須調(diào)用才能執(zhí)行內(nèi)部代碼

Block變量的聲明、賦值與調(diào)用

Block變量的聲明

Block變量的聲明格式為: 返回值類(lèi)型(^Block名字)(參數(shù)列表);

// 聲明一個(gè)無(wú)返回值,參數(shù)為兩個(gè)字符串對(duì)象,叫做aBlock的Block
void(^aBlock)(NSString *x, NSString *y);

// 形參變量名稱(chēng)可以省略,只留有變量類(lèi)型即可
void(^aBlock)(NSString *, NSString *);
注: ^被稱(chēng)作"脫字符"
Block變量的賦值

Block變量的賦值格式為: Block變量 = ^(參數(shù)列表){函數(shù)體};

aBlock = ^(NSString *x, NSString *y){
    NSLog(@"%@ love %@", x, y);
};
注: Block變量的賦值格式可以是: Block變量 = ^返回值類(lèi)型(參數(shù)列表){函數(shù)體};,不過(guò)通常情況下都將返回值類(lèi)型省略,因?yàn)榫幾g器可以從存儲(chǔ)代碼塊的變量中確定返回值的類(lèi)型
聲明Block變量的同時(shí)進(jìn)行賦值

int(^myBlock)(int) = ^(int num){
    return num * 7;
};

// 如果沒(méi)有參數(shù)列表,在賦值時(shí)參數(shù)列表可以省略
void(^aVoidBlock)() = ^{
    NSLog(@"I am a aVoidBlock");
};
Block變量的調(diào)用

// 調(diào)用后控制臺(tái)輸出"Li Lei love Han Meimei"
aBlock(@"Li Lei",@"Han Meimei");

// 調(diào)用后控制臺(tái)輸出"result = 63"
NSLog(@"result = %d", myBlock(9));

// 調(diào)用后控制臺(tái)輸出"I am a aVoidBlock"
aVoidBlock();

使用typedef定義Block類(lèi)型
在實(shí)際使用Block的過(guò)程中,我們可能需要重復(fù)地聲明多個(gè)相同返回值相同參數(shù)列表的Block變量,如果總是重復(fù)地編寫(xiě)一長(zhǎng)串代碼來(lái)聲明變量會(huì)非常繁瑣,所以我們可以使用typedef來(lái)定義Block類(lèi)型

// 定義一種無(wú)返回值無(wú)參數(shù)列表的Block類(lèi)型
typedef void(^SayHello)();

// 我們可以像OC中聲明變量一樣使用Block類(lèi)型SayHello來(lái)聲明變量
SayHello hello = ^(){
    NSLog(@"hello");
};

// 調(diào)用后控制臺(tái)輸出"hello"
hello();
Block作為函數(shù)參數(shù)

Block作為C函數(shù)參數(shù)

// 1.定義一個(gè)形參為Block的C函數(shù)
void useBlockForC(int(^aBlock)(int, int))
{
    NSLog(@"result = %d", aBlock(300,200));
}

// 2.聲明并賦值定義一個(gè)Block變量
int(^addBlock)(int, int) = ^(int x, int y){
    return x+y;
};

// 3.以Block作為函數(shù)參數(shù),把Block像對(duì)象一樣傳遞
useBlockForC(addBlock);

// 將第2點(diǎn)和第3點(diǎn)合并一起,以?xún)?nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
useBlockForC(^(int x, int y) {
    return x+y;
});
Block作為OC函數(shù)參數(shù)

1.定義一個(gè)形參為Block的OC函數(shù)

- (void)useBlockForOC:(int(^)(int, int))aBlock
{
    NSLog(@"result = %d", aBlock(300,200));
}

// 2.聲明并賦值定義一個(gè)Block變量
int(^addBlock)(int, int) = ^(int x, int y){
    return x+y;
};

// 3.以Block作為函數(shù)參數(shù),把Block像對(duì)象一樣傳遞
[self useBlockForOC:addBlock];

// 將第2點(diǎn)和第3點(diǎn)合并一起,以?xún)?nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
[self useBlockForOC:^(int x, int y){
    return x+y;
}];

使用typedef簡(jiǎn)化Block

// 1.使用typedef定義Block類(lèi)型
typedef int(^MyBlock)(int, int);

// 2.定義一個(gè)形參為Block的OC函數(shù)
- (void)useBlockForOC:(MyBlock)aBlock
{
    NSLog(@"result = %d", aBlock(300,200));
}

// 3.聲明并賦值定義一個(gè)Block變量
MyBlock addBlock = ^(int x, int y){
    return x+y;
};

// 4.以Block作為函數(shù)參數(shù),把Block像對(duì)象一樣傳遞
[self useBlockForOC:addBlock];

// 將第3點(diǎn)和第4點(diǎn)合并一起,以?xún)?nèi)聯(lián)定義的Block作為函數(shù)參數(shù)
[self useBlockForOC:^(int x, int y){
    return x+y;
}];

Block內(nèi)訪(fǎng)問(wèn)局部變量

在Block中可以訪(fǎng)問(wèn)局部變量
// 聲明局部變量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();
注: 原理解析,通過(guò)clang命令將OC轉(zhuǎn)為C++代碼來(lái)查看一下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));

// 將變量類(lèi)型精簡(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訪(fǎng)問(wèn)其FuncPtr元素,在定義Block時(shí)為FuncPtr元素傳進(jìn)去的__main_block_func_0方法
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

// __main_block_func_0方法代碼如下,由此可見(jiàn)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)訪(fǎng)問(wèn)__block修飾的局部變量

在局部變量前使用下劃線(xiàn)下劃線(xiàn)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();
在局部變量前使用下劃線(xiàn)下劃線(xiàn)block修飾,在Block中可以直接修改局部變量
// 聲明局部變量global
__block int global = 100;

void(^myBlock)() = ^{
    global ++; // 這句正確
    NSLog(@"global = %d", global);
};
// 調(diào)用后控制臺(tái)輸出"global = 101"
myBlock();
注: 原理解析,通過(guò)clang命令將OC轉(zhuǎn)為C++代碼來(lái)查看一下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));

// 將變量類(lèi)型精簡(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)訪(fǎng)問(wèn)全局變量

Block在MRC及ARC下的內(nèi)存管理

默認(rèn)情況下,Block的內(nèi)存存儲(chǔ)在棧中,不需要開(kāi)發(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í)需要開(kāi)發(fā)人員對(duì)其進(jìn)行release操作來(lái)管理內(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ì)象在這里無(wú)法正常被釋放,因?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ì)象的前面使用下劃線(xiàn)下劃線(xiàn)block來(lái)修飾

__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)部又訪(fǎng)問(wèn)了該對(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)引用無(wú)法釋放
情況二

@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ì)象在這里無(wú)法正常釋放,雖然表面看起來(lái)一個(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)引用無(wú)法釋放
如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪(fǎng)問(wèn)了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是在對(duì)象的前面使用下劃線(xiàn)下劃線(xiàn)block來(lái)修飾,以避免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)部又訪(fǎng)問(wèn)了該對(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)引用無(wú)法釋放

情況二

@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ì)象在這里無(wú)法正常釋放,在resetBlock方法實(shí)現(xiàn)中,Block內(nèi)部對(duì)self進(jìn)行了一次強(qiáng)引用,導(dǎo)致循環(huán)引用無(wú)法釋放

如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪(fǎng)問(wèn)了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是使用一個(gè)弱引用的指針指向該對(duì)象,然后在Block內(nèi)部使用該弱引用指針來(lá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)引用的辦法即在變量前使用下劃線(xiàn)下劃線(xià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)橄聞澗€(xiàn)下劃線(xiàn)block在ARC中并不能禁止Block對(duì)所引用的對(duì)象進(jìn)行強(qiáng)引用,解決辦法可以是在Block中將myController置空(為了可以修改myController,還是需要使用下劃線(xiàn)下劃線(xiàn)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è)弱指針來(lái)指向該對(duì)象,并將該弱指針?lè)旁贐lock中使用,這樣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è)問(wèn)題,因?yàn)锽lock是通過(guò)弱引用指向了myController對(duì)象,那么有可能在調(diào)用Block之前myController對(duì)象便已經(jīng)被釋放了,所以我們需要在Block內(nèi)部再定義一個(gè)強(qiáng)指針來(lái)指向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ì)其并沒(méi)有強(qiáng)引用關(guān)系,且在ARC中只需要避免循環(huán)引用即可,如果只是Block單方面地對(duì)外部變量進(jìn)行強(qiáng)引用,并不會(huì)造成內(nèi)存泄漏

注: 關(guān)于下劃線(xiàn)下劃線(xiàn)block關(guān)鍵字在MRC和ARC下的不同

__block在MRC下有兩個(gè)作用
1. 允許在Block中訪(fǎng)問(wèn)和修改局部變量 
2. 禁止Block對(duì)所引用的對(duì)象進(jìn)行隱式retain操作

__block在ARC下只有一個(gè)作用
1. 允許在Block中訪(fǎng)問(wèn)和修改局部變量

使用Block進(jìn)行排序

在開(kāi)發(fā)中,我們一般使用數(shù)組的如下兩個(gè)方法來(lái)進(jìn)行排序

不可變數(shù)組的方法: - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr
可變數(shù)組的方法 : - (void)sortUsingComparator:(NSComparator)cmptr
其中,NSComparator是利用typedef定義的Block類(lèi)型

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
其中,這個(gè)返回值為NSComparisonResult枚舉,這個(gè)返回值用來(lái)決定Block的兩個(gè)參數(shù)順序,我們只需在Block中指明不同條件下Block的兩個(gè)參數(shù)的順序即可,方法內(nèi)部會(huì)將數(shù)組中的元素分別利用Block來(lái)進(jìn)行比較并排序

typedef NS_ENUM(NSInteger, NSComparisonResult)
{
    NSOrderedAscending = -1L, // 升序,表示左側(cè)的字符在右側(cè)的字符前邊
    NSOrderedSame, // 相等
    NSOrderedDescending // 降序,表示左側(cè)的字符在右側(cè)的字符后邊
};
我們以Person類(lèi)為例,對(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; // 在這里返回降序,說(shuō)明在該種條件下,obj1排在obj2的后邊
    }
    else if (stu1.age < stu2.age)
    {
        return NSOrderedAscending;
    }
    else
    {
        return NSOrderedSame;
    }
}];
```明出處。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昌讲,一起剝皮案震驚了整個(gè)濱河市国夜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌短绸,老刑警劉巖车吹,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筹裕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡窄驹,警方通過(guò)查閱死者的電腦和手機(jī)饶碘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)馒吴,“玉大人扎运,你說(shuō)我怎么就攤上這事∫粒” “怎么了豪治?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扯罐。 經(jīng)常有香客問(wèn)我负拟,道長(zhǎng),這世上最難降的妖魔是什么歹河? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任掩浙,我火速辦了婚禮,結(jié)果婚禮上秸歧,老公的妹妹穿的比我還像新娘厨姚。我一直安慰自己,他們只是感情好键菱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布谬墙。 她就那樣靜靜地躺著,像睡著了一般经备。 火紅的嫁衣襯著肌膚如雪拭抬。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天侵蒙,我揣著相機(jī)與錄音造虎,去河邊找鬼。 笑死纷闺,一個(gè)胖子當(dāng)著我的面吹牛算凿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播急但,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼澎媒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了波桩?” 一聲冷哼從身側(cè)響起戒努,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后储玫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體侍筛,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年撒穷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匣椰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡端礼,死狀恐怖禽笑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛤奥,我是刑警寧澤佳镜,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凡桥,受9級(jí)特大地震影響蟀伸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缅刽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一啊掏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衰猛,春花似錦迟蜜、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至冕杠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酸茴,已是汗流浹背分预。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留薪捍,地道東北人笼痹。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像酪穿,于是被迫代替她去往敵國(guó)和親凳干。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • iOS代碼塊Block 概述 代碼塊Block是蘋(píng)果在iOS4開(kāi)始引入的對(duì)C語(yǔ)言的擴(kuò)展,用來(lái)實(shí)現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,349評(píng)論 2 26
  • Block使用場(chǎng)景被济,可以在兩個(gè)界面的傳值救赐,也可以對(duì)代碼封裝作為參數(shù)的傳遞等。用過(guò)GCD就知道Block的精妙之處只磷。...
    Coder_JMicheal閱讀 724評(píng)論 2 1
  • iOS代碼塊Block 概述 代碼塊Block是蘋(píng)果在iOS4開(kāi)始引入的對(duì)C語(yǔ)言的擴(kuò)展,用來(lái)實(shí)現(xiàn)匿名函數(shù)的特性,B...
    蚊香醬閱讀 59,376評(píng)論 61 440
  • 前言 Blocks是C語(yǔ)言的擴(kuò)充功能经磅,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,768評(píng)論 0 23
  • 工具命令轉(zhuǎn)化C++xcrun -sdk iphoneos clang -arch arm64 -rewrite-o...
    iYeso閱讀 3,127評(píng)論 6 67