Block

block.png

iOS代碼塊Block

概述

代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實現匿名函數的特性,Block是一種特殊的數據類型,其可以正常定義變量、作為參數、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時候調用,目前Block已經廣泛應用于iOS開發中,常用于GCD、動畫、排序及各類回調

注: Block的聲明與賦值只是保存了一段代碼段,必須調用才能執行內部代碼

Block變量的聲明、賦值與調用

Block變量的聲明

Block變量的聲明格式為: 返回值類型(^Block名字)(參數列表);// 聲明一個無返回值,參數為兩個字符串對象,叫做aBlock的Blockvoid(^aBlock)(NSString*x,NSString*y);// 形參變量名稱可以省略,只留有變量類型即可void(^aBlock)(NSString*,NSString*);

注: ^被稱作"脫字符"

Block變量的賦值

Block變量的賦值格式為: Block變量 = ^(參數列表){函數體};aBlock = ^(NSString*x,NSString*y){NSLog(@"%@ love %@", x, y);};

注: Block變量的賦值格式可以是: Block變量 = ^返回值類型(參數列表){函數體};,不過通常情況下都將返回值類型省略,因為編譯器可以從存儲代碼塊的變量中確定返回值的類型

聲明Block變量的同時進行賦值

int(^myBlock)(int) = ^(intnum){returnnum *7;};// 如果沒有參數列表,在賦值時參數列表可以省略void(^aVoidBlock)() = ^{NSLog(@"I am a aVoidBlock");};

Block變量的調用

// 調用后控制臺輸出"Li Lei love Han Meimei"aBlock(@"Li Lei",@"Han Meimei");// 調用后控制臺輸出"result = 63"NSLog(@"result = %d", myBlock(9));// 調用后控制臺輸出"I am a aVoidBlock"aVoidBlock();

使用typedef定義Block類型

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

// 定義一種無返回值無參數列表的Block類型typedefvoid(^SayHello)();// 我們可以像OC中聲明變量一樣使用Block類型SayHello來聲明變量SayHello hello = ^(){NSLog(@"hello");};// 調用后控制臺輸出"hello"hello();

Block作為函數參數

Block作為C函數參數

// 1.定義一個形參為Block的C函數voiduseBlockForC(int(^aBlock)(int,int)){? ? NSLog(@"result = %d", aBlock(300,200));}// 2.聲明并賦值定義一個Block變量int(^addBlock)(int,int) = ^(intx,inty){returnx+y;};// 3.以Block作為函數參數,把Block像對象一樣傳遞useBlockForC(addBlock);// 將第2點和第3點合并一起,以內聯定義的Block作為函數參數useBlockForC(^(intx,inty) {returnx+y;});

Block作為OC函數參數

// 1.定義一個形參為Block的OC函數- (void)useBlockForOC:(int(^)(int,int))aBlock{NSLog(@"result = %d", aBlock(300,200));}// 2.聲明并賦值定義一個Block變量int(^addBlock)(int,int) = ^(intx,inty){returnx+y;};// 3.以Block作為函數參數,把Block像對象一樣傳遞[selfuseBlockForOC:addBlock];// 將第2點和第3點合并一起,以內聯定義的Block作為函數參數[selfuseBlockForOC:^(intx,inty){returnx+y;}];

使用typedef簡化Block

// 1.使用typedef定義Block類型typedefint(^MyBlock)(int,int);// 2.定義一個形參為Block的OC函數- (void)useBlockForOC:(MyBlock)aBlock{NSLog(@"result = %d", aBlock(300,200));}// 3.聲明并賦值定義一個Block變量MyBlock addBlock = ^(intx,inty){returnx+y;};// 4.以Block作為函數參數,把Block像對象一樣傳遞[selfuseBlockForOC:addBlock];// 將第3點和第4點合并一起,以內聯定義的Block作為函數參數[selfuseBlockForOC:^(intx,inty){returnx+y;}];

Block內訪問局部變量

在Block中可以訪問局部變量

// 聲明局部變量globalintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 100"myBlock();

在聲明Block之后、調用Block之前對局部變量進行修改,在調用Block時局部變量值是修改之前的舊值

// 聲明局部變量globalintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};global =101;// 調用后控制臺輸出"global = 100"myBlock();

在Block中不可以直接修改局部變量

// 聲明局部變量globalintglobal =100;void(^myBlock)() = ^{? ? global ++;// 這句報錯NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 100"myBlock();

注: 原理解析,通過clang命令將OC轉為C++代碼來查看一下Block底層實現,clang命令使用方式為終端使用cd定位到main.m文件所在文件夾,然后利用clang -rewrite-objc main.m將OC轉為C++,成功后在main.m同目錄下會生成一個main.cpp文件

// OC代碼如下void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 轉為C++代碼如下void(*myBlock)() = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, global));// 將變量類型精簡之后C++代碼如下,我們發現Block變量實際上就是一個指向結構體__main_block_impl_0的指針,而結構體的第三個元素是局部變量global的值void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, global);// 我們看一下結構體__main_block_impl_0的代碼struct__main_block_impl_0 {struct__block_impl impl;struct__main_block_desc_0* Desc;intglobal;__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,int_global,intflags=0) : global(_global) {? ? impl.isa = &_NSConcreteStackBlock;? ? impl.Flags = flags;? ? impl.FuncPtr = fp;? ? Desc = desc;? }};// 在OC中調用Block的方法轉為C++代碼如下,實際上是指向結構體的指針myBlock訪問其FuncPtr元素,在定義Block時為FuncPtr元素傳進去的__main_block_func_0方法((void(*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);// __main_block_func_0方法代碼如下,由此可見NSLog的global正是定義Block時為結構體傳進去的局部變量global的值staticvoid__main_block_func_0(struct__main_block_impl_0 *__cself) {intglobal = __cself->global;// bound by copyNSLog((NSString*)&__NSConstantStringImpl__var_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_d5d9eb_mi_0, global);}// 由此可知,在Block定義時便是將局部變量的值傳給Block變量所指向的結構體,因此在調用Block之前對局部變量進行修改并不會影響Block內部的值,同時內部的值也是不可修改的

Block內訪問__block修飾的局部變量

在局部變量前使用下劃線下劃線block修飾,在聲明Block之后、調用Block之前對局部變量進行修改,在調用Block時局部變量值是修改之后的新值

// 聲明局部變量global__blockintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};global =101;// 調用后控制臺輸出"global = 101"myBlock();

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

// 聲明局部變量global__blockintglobal =100;void(^myBlock)() = ^{? ? global ++;// 這句正確NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 101"myBlock();

注: 原理解析,通過clang命令將OC轉為C++代碼來查看一下Block底層實現

// OC代碼如下void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 轉為C++代碼如下void(*myBlock)() = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_global_0 *)&global,570425344));// 將變量類型精簡之后C++代碼如下,我們發現Block變量實際上就是一個指向結構體__main_block_impl_0的指針,而結構體的第三個元素是局部變量global的指針void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &global,570425344);// 由此可知,在局部變量前使用__block修飾,在Block定義時便是將局部變量的指針傳給Block變量所指向的結構體,因此在調用Block之前對局部變量進行修改會影響Block內部的值,同時內部的值也是可以修改的

Block內訪問全局變量

在Block中可以訪問全局變量

// 聲明全局變量globalintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 100"myBlock();

在聲明Block之后、調用Block之前對全局變量進行修改,在調用Block時全局變量值是修改之后的新值

// 聲明全局變量globalintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};global =101;// 調用后控制臺輸出"global = 101"myBlock();

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

// 聲明全局變量globalintglobal =100;void(^myBlock)() = ^{? ? global ++;NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 101"myBlock();

注: 原理解析,通過clang命令將OC轉為C++代碼來查看一下Block底層實現

// OC代碼如下void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 轉為C++代碼如下void(*myBlock)() = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA));// 將變量類型精簡之后C++代碼如下,我們發現Block變量實際上就是一個指向結構體__main_block_impl_0的指針,而結構體中并未保存全局變量global的值或者指針void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);// 我們看一下結構體__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,intflags=0) {? ? impl.isa = &_NSConcreteStackBlock;? ? impl.Flags = flags;? ? impl.FuncPtr = fp;? ? Desc = desc;? }};// 在OC中調用Block的方法轉為C++代碼如下,實際上是指向結構體的指針myBlock訪問其FuncPtr元素,在定義Block時為FuncPtr元素傳進去的__main_block_func_0方法((void(*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);// __main_block_func_0方法代碼如下,由此可見NSLog的global還是全局變量global的值staticvoid__main_block_func_0(struct__main_block_impl_0 *__cself) {NSLog((NSString*)&__NSConstantStringImpl__var_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_f35954_mi_0, global);}// 由此可知,全局變量所占用的內存只有一份,供所有函數共同調用,在Block定義時并未將全局變量的值或者指針傳給Block變量所指向的結構體,因此在調用Block之前對局部變量進行修改會影響Block內部的值,同時內部的值也是可以修改的

Block內訪問靜態變量

在Block中可以訪問靜態變量

// 聲明靜態變量globalstaticintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 100"myBlock();

在聲明Block之后、調用Block之前對靜態變量進行修改,在調用Block時靜態變量值是修改之后的新值

// 聲明靜態變量globalstaticintglobal =100;void(^myBlock)() = ^{NSLog(@"global = %d", global);};global =101;// 調用后控制臺輸出"global = 101"myBlock();

在Block中可以直接修改靜態變量

// 聲明靜態變量globalstaticintglobal =100;void(^myBlock)() = ^{? ? global ++;NSLog(@"global = %d", global);};// 調用后控制臺輸出"global = 101"myBlock();

注: 原理解析,通過clang命令將OC轉為C++代碼來查看一下Block底層實現

// OC代碼如下void(^myBlock)() = ^{NSLog(@"global = %d", global);};// 轉為C++代碼如下void(*myBlock)() = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, &global));// 將變量類型精簡之后C++代碼如下,我們發現Block變量實際上就是一個指向結構體__main_block_impl_0的指針,而結構體的第三個元素是靜態變量global的指針void(*myBlock)() = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &global);// 我們看一下結構體__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,intflags=0) : global(_global) {? ? impl.isa = &_NSConcreteStackBlock;? ? impl.Flags = flags;? ? impl.FuncPtr = fp;? ? Desc = desc;? }};// 在OC中調用Block的方法轉為C++代碼如下,實際上是指向結構體的指針myBlock訪問其FuncPtr元素,在定義Block時為FuncPtr元素傳進去的__main_block_func_0方法((void(*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);// __main_block_func_0方法代碼如下,由此可見NSLog的global正是定義Block時為結構體傳進去的靜態變量global的指針staticvoid__main_block_func_0(struct__main_block_impl_0 *__cself) {int*global = __cself->global;// bound by copyNSLog((NSString*)&__NSConstantStringImpl__var_folders_6y_vkd9wnv13pz6lc_h8phss0jw0000gn_T_main_4d124d_mi_0, (*global));}// 由此可知,在Block定義時便是將靜態變量的指針傳給Block變量所指向的結構體,因此在調用Block之前對靜態變量進行修改會影響Block內部的值,同時內部的值也是可以修改的

Block在MRC及ARC下的內存管理

Block在MRC下的內存管理

默認情況下,Block的內存存儲在棧中,不需要開發人員對其進行內存管理

// 當Block變量出了作用域,Block的內存會被自動釋放void(^myBlock)() = ^{NSLog(@"------");};myBlock();

在Block的內存存儲在棧中時,如果在Block中引用了外面的對象,不會對所引用的對象進行任何操作

Person *p = [[Person alloc] init];void(^myBlock)() = ^{NSLog(@"------%@", p);};myBlock();? ? ? ? [p release];// Person對象在這里可以正常被釋放

如果對Block進行一次copy操作,那么Block的內存會被移動到堆中,這時需要開發人員對其進行release操作來管理內存

void(^myBlock)() = ^{NSLog(@"------");};myBlock();? ? ? ? Block_copy(myBlock);// do something ...Block_release(myBlock);

如果對Block進行一次copy操作,那么Block的內存會被移動到堆中,在Block的內存存儲在堆中時,如果在Block中引用了外面的對象,會對所引用的對象進行一次retain操作,即使在Block自身調用了release操作之后,Block也不會對所引用的對象進行一次release操作,這時會造成內存泄漏

Person *p = [[Person alloc] init];void(^myBlock)() = ^{NSLog(@"------%@", p);};myBlock();? ? ? ? Block_copy(myBlock);// do something ...Block_release(myBlock);? ? ? ? [p release];// Person對象在這里無法正常被釋放,因為其在Block中被進行了一次retain操作

如果對Block進行一次copy操作,那么Block的內存會被移動到堆中,在Block的內存存儲在堆中時,如果在Block中引用了外面的對象,會對所引用的對象進行一次retain操作,為了不對所引用的對象進行一次retain操作,可以在對象的前面使用下劃線下劃線block來修飾

__block Person *p = [[Person alloc] init];void(^myBlock)() = ^{NSLog(@"------%@", p);};myBlock();? ? ? ? Block_copy(myBlock);// do something ...Block_release(myBlock);? ? ? ? [p release];// Person對象在這里可以正常被釋放

如果對象內部有一個Block屬性,而在Block內部又訪問了該對象,那么會造成循環引用

情況一

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();@end@implementationPerson- (void)dealloc{NSLog(@"Person dealloc");? ? ? ? Block_release(_myBlock);? ? [superdealloc];}@endPerson *p = [[Person alloc] init];? ? ? ? p.myBlock = ^{NSLog(@"------%@", p);};p.myBlock();? ? ? ? [p release];// 因為myBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統釋放),所以Block會對Person對象進行一次retain操作,導致循環引用無法釋放

情況二

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();- (void)resetBlock;@end@implementationPerson- (void)resetBlock{self.myBlock = ^{NSLog(@"------%@",self);? ? };}- (void)dealloc{NSLog(@"Person dealloc");? ? ? ? Block_release(_myBlock);? ? ? ? [superdealloc];}@endPerson *p = [[Person alloc] init];[p resetBlock];[p release];// Person對象在這里無法正常釋放,雖然表面看起來一個alloc對應一個release符合內存管理規則,但是實際在resetBlock方法實現中,Block內部對self進行了一次retain操作,導致循環引用無法釋放

如果對象內部有一個Block屬性,而在Block內部又訪問了該對象,那么會造成循環引用,解決循環引用的辦法是在對象的前面使用下劃線下劃線block來修飾,以避免Block對對象進行retain操作

情況一

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();@end@implementationPerson- (void)dealloc{NSLog(@"Person dealloc");? ? ? ? Block_release(_myBlock);? ? [superdealloc];}@end__block Person *p = [[Person alloc] init];? ? ? ? p.myBlock = ^{NSLog(@"------%@", p);};p.myBlock();? ? ? ? [p release];// Person對象在這里可以正常被釋放

情況二

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();- (void)resetBlock;@end@implementationPerson- (void)resetBlock{// 這里為了通用一點,可以使用__block typeof(self) p = self;__block Person *p =self;self.myBlock = ^{NSLog(@"------%@", p);? ? };}- (void)dealloc{NSLog(@"Person dealloc");? ? ? ? Block_release(_myBlock);? ? ? ? [superdealloc];}@endPerson *p = [[Person alloc] init];[p resetBlock];[p release];// Person對象在這里可以正常被釋放

Block在ARC下的內存管理

在ARC默認情況下,Block的內存存儲在堆中,ARC會自動進行內存管理,程序員只需要避免循環引用即可

// 當Block變量出了作用域,Block的內存會被自動釋放void(^myBlock)() = ^{NSLog(@"------");};myBlock();

在Block的內存存儲在堆中時,如果在Block中引用了外面的對象,會對所引用的對象進行強引用,但是在Block被釋放時會自動去掉對該對象的強引用,所以不會造成內存泄漏

Person *p = [[Person alloc] init];void(^myBlock)() = ^{NSLog(@"------%@", p);};myBlock();// Person對象在這里可以正常被釋放

如果對象內部有一個Block屬性,而在Block內部又訪問了該對象,那么會造成循環引用

情況一

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();@end@implementationPerson- (void)dealloc{NSLog(@"Person dealloc");}@endPerson *p = [[Person alloc] init];? ? ? ? p.myBlock = ^{NSLog(@"------%@", p);};p.myBlock();// 因為myBlock作為Person的屬性,采用copy修飾符修飾(這樣才能保證Block在堆里面,以免Block在棧中被系統釋放),所以Block會對Person對象進行一次強引用,導致循環引用無法釋放

情況二

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();- (void)resetBlock;@end@implementationPerson- (void)resetBlock{self.myBlock = ^{NSLog(@"------%@",self);? ? };}- (void)dealloc{NSLog(@"Person dealloc");}@endPerson *p = [[Person alloc] init];[p resetBlock];// Person對象在這里無法正常釋放,在resetBlock方法實現中,Block內部對self進行了一次強引用,導致循環引用無法釋放

如果對象內部有一個Block屬性,而在Block內部又訪問了該對象,那么會造成循環引用,解決循環引用的辦法是使用一個弱引用的指針指向該對象,然后在Block內部使用該弱引用指針來進行操作,這樣避免了Block對對象進行強引用

情況一

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();@end@implementationPerson- (void)dealloc{NSLog(@"Person dealloc");}@endPerson *p = [[Person alloc] init];__weaktypeof(p) weakP = p;p.myBlock = ^{NSLog(@"------%@", weakP);};p.myBlock();// Person對象在這里可以正常被釋放

情況二

@interfacePerson:NSObject@property(nonatomic,copy)void(^myBlock)();- (void)resetBlock;@end@implementationPerson- (void)resetBlock{// 這里為了通用一點,可以使用__weak typeof(self) weakP = self;__weakPerson *weakP =self;self.myBlock = ^{NSLog(@"------%@", weakP);? ? };}- (void)dealloc{NSLog(@"Person dealloc");}@endPerson *p = [[Person alloc] init];[p resetBlock];// Person對象在這里可以正常被釋放

Block在ARC下的內存管理的官方案例

在MRC中,我們從當前控制器采用模態視圖方式present進入MyViewController控制器,在Block中會對myViewController進行一次retain操作,造成循環引用

MyViewController *myController = [[MyViewController alloc] init];// ...myController.completionHandler =? ^(NSIntegerresult) {? [myController dismissViewControllerAnimated:YEScompletion:nil];};[selfpresentViewController:myController animated:YEScompletion:^{? [myController release];}];

在MRC中解決循環引用的辦法即在變量前使用下劃線下劃線block修飾,禁止Block對所引用的對象進行retain操作

__block MyViewController *myController = [[MyViewController alloc] init];// ...myController.completionHandler =? ^(NSIntegerresult) {? ? [myController dismissViewControllerAnimated:YEScompletion:nil];};[selfpresentViewController:myController animated:YEScompletion:^{? [myController release];}];

但是上述方法在ARC下行不通,因為下劃線下劃線block在ARC中并不能禁止Block對所引用的對象進行強引用,解決辦法可以是在Block中將myController置空(為了可以修改myController,還是需要使用下劃線下劃線block對變量進行修飾)

__block MyViewController *myController = [[MyViewController alloc] init];// ...myController.completionHandler =? ^(NSIntegerresult) {? ? [myController dismissViewControllerAnimated:YEScompletion:nil];? ? myController =nil;};[selfpresentViewController:myController animated:YEScompletion:^{}];

上述方法確實可以解決循環引用,但是在ARC中還有更優雅的解決辦法,新創建一個弱指針來指向該對象,并將該弱指針放在Block中使用,這樣Block便不會造成循環引用

MyViewController *myController = [[MyViewController alloc] init];// ...__weakMyViewController *weakMyController = myController;myController.completionHandler =? ^(NSIntegerresult) {? ? [weakMyController dismissViewControllerAnimated:YEScompletion:nil];};[selfpresentViewController:myController animated:YEScompletion:^{}];

雖然解決了循環引用,但是也容易涉及到另一個問題,因為Block是通過弱引用指向了myController對象,那么有可能在調用Block之前myController對象便已經被釋放了,所以我們需要在Block內部再定義一個強指針來指向myController對象

MyViewController *myController = [[MyViewController alloc] init];// ...__weakMyViewController *weakMyController = myController;myController.completionHandler =? ^(NSIntegerresult) {? ? MyViewController *strongMyController = weakMyController;if(strongMyController)? ? {? ? ? ? [strongMyController dismissViewControllerAnimated:YEScompletion:nil];? ? }else{// Probably nothing...}};[selfpresentViewController:myController animated:YEScompletion:^{}];

這里需要補充一下,在Block內部定義的變量,會在作用域結束時自動釋放,Block對其并沒有強引用關系,且在ARC中只需要避免循環引用即可,如果只是Block單方面地對外部變量進行強引用,并不會造成內存泄漏

注: 關于下劃線下劃線block關鍵字在MRC和ARC下的不同

__block在MRC下有兩個作用1.允許在Block中訪問和修改局部變量2.禁止Block對所引用的對象進行隱式retain操作__block在ARC下只有一個作用1.允許在Block中訪問和修改局部變量

使用Block進行排序

在開發中,我們一般使用數組的如下兩個方法來進行排序

不可變數組的方法: - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr

可變數組的方法 : - (void)sortUsingComparator:(NSComparator)cmptr

其中,NSComparator是利用typedef定義的Block類型

typedefNSComparisonResult(^NSComparator)(idobj1,idobj2);

其中,這個返回值為NSComparisonResult枚舉,這個返回值用來決定Block的兩個參數順序,我們只需在Block中指明不同條件下Block的兩個參數的順序即可,方法內部會將數組中的元素分別利用Block來進行比較并排序

typedefNS_ENUM(NSInteger,NSComparisonResult){NSOrderedAscending=-1L,// 升序,表示左側的字符在右側的字符前邊NSOrderedSame,// 相等NSOrderedDescending// 降序,表示左側的字符在右側的字符后邊};

我們以Person類為例,對Person對象以年齡升序進行排序,具體方法如下

@interfaceStudent:NSObject@property(nonatomic,assign)intage;@end@implementationStudent@endStudent *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(idobj1,idobj2) {? ? Student *stu1 = obj1;? ? Student *stu2 = obj2;if(stu1.age > stu2.age)? ? {returnNSOrderedDescending;// 在這里返回降序,說明在該種條件下,obj1排在obj2的后邊}elseif(stu1.age < stu2.age)? ? {returnNSOrderedAscending;? ? }else{returnNSOrderedSame;? ? }}];

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 如果有一天,你找不到我 你會去什么地方發呆 給你唱首歌,陪我到可可西里去看海 誰說月亮上不曾有青草 誰說可可西里沒...
    wuli路遙閱讀 376評論 0 1
  • 大冰這個大痞子 資深大混混 相對于他的民謠更喜歡他的書 從13年《他們最幸福》 到14年《乖,摸摸頭》 到15年《...
    大頭諾阿諾閱讀 515評論 0 2
  • 這個冬天,是我有史以來過的最冷的一個,心里裝著太多的東西。一個人在校園里瞎晃悠,冷風吹得我直哆嗦,慢慢的,吹落了兩...
    這么大風徐中良閱讀 443評論 0 2
  • 感覺實習半年什么東西都沒學到,畢業生剛開始最珍貴的半年已經被我白白浪費了,被海馬坑死了,讓本科生實習半年多,一點有...
    星期一說它不想升旗閱讀 214評論 0 0