Block的定義和使用

概述

在C#異步編程時(shí)我們經(jīng)常進(jìn)行函數(shù)回調(diào),由于函數(shù)調(diào)用時(shí)異步執(zhí)行的,如果我們想讓一個(gè)操作執(zhí)行完成之后再執(zhí)行另一個(gè)函數(shù),則無法按照正常的代碼書寫順序進(jìn)行編程(同步線程),因?yàn)槲覀儫o法知道前一個(gè)方法什么時(shí)候執(zhí)行結(jié)束,此時(shí),,我們就會(huì)用到一名委托或者lambda表達(dá)式將一個(gè)操作作為一個(gè)參數(shù)進(jìn)行傳遞。在OC中也有類似的方法,,我們稱之為代碼塊(Block)。Block就是一個(gè)函數(shù)體(匿名函數(shù)),它是ObjC對(duì)于閉包的實(shí)現(xiàn),在塊狀中我們可以持有或引用局部變量(不禁想到了lambda表達(dá)式),同時(shí)利用Block你可以將一個(gè)操作作為一個(gè)參數(shù)進(jìn)行傳遞(是不是想起了C語言中的函數(shù)指針)。

總結(jié):

簡單來說,Block其實(shí)就是一個(gè)代碼塊,把你想要執(zhí)行的代碼封裝在代碼塊里,等到需要的時(shí)候再去調(diào)用。(Block在定義時(shí)并不會(huì)執(zhí)行內(nèi)部的代碼,只有在調(diào)用時(shí)才會(huì)執(zhí)行)。

Block代碼塊和普通函數(shù)都是一段代碼,兩者有什么區(qū)別?
官方描述

解釋

Block代碼:是一個(gè)函數(shù)對(duì)象,是在程序運(yùn)行過程中產(chǎn)生的;
普通函數(shù):是一段固定代碼,產(chǎn)生于編譯期;

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

1、Block變量的聲明

返回值類型(^Block名字)(參數(shù)列表);
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
void (^myBlock1)(void);  //無返回值,無參數(shù)
void (^myBlock2)(NSObject, int); //無返回值,有參數(shù)
void(^myBlock3)(NSString *, NSString *);  // 形參變量名稱可以省略,只留有變量類型即可  
NSString* (^myBlock4)(NSString *x,NSString *y); //有返回值和參數(shù),并且在參數(shù)類型后面加入了參數(shù)名(僅為可讀性)

注1: Block的聲明與賦值只是保存了一段代碼段,必須調(diào)用才能執(zhí)行內(nèi)部代碼
注2: ^被稱作"脫字符"
注3:形參變量名稱可以省略,只留有變量類型即可
Block變量的賦值

Block變量的賦值格式為:

Block變量 = ^(參數(shù)列表){函數(shù)體};
myBlock4 = ^(NSString *x, NSString *y){
NSLog(@“x:%, y:%@“, x, y);
};

注: Block變量的賦值格式可以是: Block變量 = ^返回值類型(參數(shù)列表){函數(shù)體};,不過通常情況下都將返回值類型省略,因?yàn)榫幾g器可以從存儲(chǔ)代碼塊的變量中確定返回值的類型

聲明Block變量的同時(shí)進(jìn)行賦值

int(^myBlock)(int) = ^(int num){
return num*7;
};
// 如果沒有參數(shù)列表,在賦值時(shí)參數(shù)列表可以省略
void(myBlock1)(void)={
NSLog(@"我是一個(gè)代碼塊");
};
Block變量的調(diào)用
// 調(diào)用后 NSLog(@"我是一個(gè)代碼塊");
myBlock1();

使用typedef定義Block類型

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

//定義一種返回參數(shù)列表的Block類型。
typedef void(^Sayhello)(void);
//定義Block對(duì)象
@property (copy, nonatomic) Sayhello sayBlock;
//我們可以像OC中聲明變量一樣使用Block類型SayHello來聲明變量
self.sayBlock = ^{
NSLog(@"hello");
};
//調(diào)用后控制臺(tái)輸出hello
self.sayBlock();

Block作為函數(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);

4、 將第2點(diǎn)和第3點(diǎn)合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)

useBlockForC(^(int x, int y) {
return x+y;
});

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

1.使用typedef定義Block類型

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];

  1. 將第3點(diǎn)和第4點(diǎn)合并一起,以內(nèi)聯(lián)定義的Block作為函數(shù)參數(shù)

[self useBlockForOC:^(int x, int y){
return x+y;
}];

Block內(nèi)訪問局部變量
  1. 在Block中可以訪問局部變量

//// 聲明局部變量global
int global = 100;
void(dBlock)(void)={
NSLog(@"%d",global);
};
//調(diào)用后控制臺(tái)輸出:100
dBlock();

2.在聲明Block之后、調(diào)用Block之前對(duì)局部變量進(jìn)行修改,在調(diào)用Block時(shí)局部變量值是修改之前的舊值

 int global = 100;
  void(^eBlock)(void) = ^{
     NSLog(@"global=%d",global);
};
global = 101;

調(diào)用后輸出100
eBlock();

  1. 在Block中不可以直接修改局部變量
     int global = 100;
     void(^fBlock)(void) = ^{
          global++; //報(bào)錯(cuò) 原因百度
         NSLog(@"global=%d",global);
       };
     fBlock();
    
Block內(nèi)訪問__block修飾的局部變量

1、在局部變量前使用下劃線下劃線block修飾,在聲明Block之后、調(diào)用Block之前對(duì)局部變量進(jìn)行修改,在調(diào)用Block時(shí)局部變量值是修改之后的新值

  __block int global = 100;
void(^gBlock)(void) = ^{
     NSLog(@"global=%d",global);
 };
     global = 101;
   //調(diào)用后輸出101
     gBlock();

2、在局部變量前使用下劃線下劃線block修飾,在Block中可以直接修改局部變量

// 聲明局部變量global
 __block int global = 100;
void(^hBlock)(void) = ^{
global ++; // 這句正確
NSLog(@"global = %d", global);
};
 // 調(diào)用后控制臺(tái)輸出"global = 101"
hBlock();
Block內(nèi)訪問全局變量
{
// 聲明全局變量global
int global1 ;
}
  1. Block訪問全局變量
global1 = 100;
void(^hBlock)(void) = ^{
NSLog(@"global = %d", global1);
};
// 調(diào)用后控制臺(tái)輸出"global = 101"
hBlock();

2.在聲明Block之后、調(diào)用Block之前對(duì)全局變量進(jìn)行修改,在調(diào)用Block時(shí)全局變量值是修改之后的新值

global1 = 100;
void(^hBlock)(void) = ^{
NSLog(@"global = %d", global1);
};
global1 = 101;
// 調(diào)用后控制臺(tái)輸出"global = 101"
hBlock();

3.在Block中可以直接修改全局變量

global1 = 100;
void(^hBlock)(void) = ^{
global1 ++;
NSLog(@"global = %d", global1);
};
 // 調(diào)用后控制臺(tái)輸出"global = 101"
hBlock();
Block內(nèi)訪問靜態(tài)變量

1.在Block中可以訪問靜態(tài)變量

//定義一個(gè)靜態(tài)全局變量
static int global2 = 100;
void(^fBlock)(void) = ^{
NSLog(@"global的值為:%d",global2);
};
//調(diào)用后控制臺(tái)輸出global的值為:100
fBlock();

2.在聲明Block之后、調(diào)用Block之前對(duì)靜態(tài)變量進(jìn)行修改,在調(diào)用Block時(shí)靜態(tài)變量值是修改之后的新值

//定義一個(gè)靜態(tài)全局變量
static int global2 = 100;
void(^fBlock)(void) = ^{
NSLog(@"global的值為:%d",global2);
};
global2 = 101;
//調(diào)用后控制臺(tái)輸出global的值為:101
fBlock();

3.在Block中可以直接修改靜態(tài)變量

//定義一個(gè)靜態(tài)全局變量
static int global2 = 100;
 void(^fBlock)(void) = ^{
global2 ++;
NSLog(@"global的值為:%d",global2);
};
//調(diào)用后控制臺(tái)輸出global的值為:101
fBlock();
lock在MRC及ARC下的內(nèi)存管理

1.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ì)象在這里可以正常被釋放

循環(huán)引用:
如果對(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)引用無法釋放

解決循環(huán)引用的方案
如果對(duì)象內(nèi)部有一個(gè)Block屬性,而在Block內(nèi)部又訪問了該對(duì)象,那么會(huì)造成循環(huán)引用,解決循環(huán)引用的辦法是在對(duì)象的前面使用下劃線下劃線block來修飾,以避免Block對(duì)對(duì)象進(jìn)行retain操作
情況一

@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;
}
}];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容