標籤:
[精通Objective-C]塊(block)
參考書籍:《精通Objective-C》【美】 Keith Lee
目錄
- 精通Objective-C塊block
- 目錄
- 塊的文法
- 塊的詞彙範圍
- 塊的記憶體管理
- 塊的使用
塊的文法
塊是一個實現的閉包,一個允許訪問其常規範圍之外變數的函數。此外,一個Objective-C塊實際上就是一個對象,它是NSObject類的子類,擁有NSObject類的相關屬性。
塊的聲明:
int (^oneParamBlock)(int); // 聲明一個名為oneParamBlock的塊,該塊接收一個int型參數,傳回值也是int型的 void (^twoParamBlock)(int,int); // 接收兩個int型參數,無傳回值 void (^twoParamBlock2)(int parm1, int parm2); // 聲明中可以含有參數名稱 int (^noParamBlock)(void); // 沒有參數需要寫上void,不能省略
塊的定義和調用:
// 將塊常量賦值給之前聲明的塊(參數與傳回值類型均需一致) oneParamBlock = ^(int addend){ return addend + 1; }; // 將塊的聲明和定義組合到一起(如果塊常量沒有參數,可以省略) void (^noParamBlock2)(void) = ^{ NSLog(@"Hello,World!"); }; // 塊的調用 int value = oneParamBlock(5); NSLog(@"%d",value); // 定義並調用塊運算式 ^(NSString *user){ NSLog(@"Greetings,%@!",user); }(@"Earthing");
將塊常量運算式用作調用方法的參數:
// 建立一個名為AdderBlock的塊類型用作方法中的參數類型typedef int (^AdderBlock)(int);@interface Calculator : NSObject-(int)process:(int)count withBlock:(AdderBlock)adder;@end@implementation Calculator-(int)process:(int)count withBlock:(AdderBlock)adder{ return adder(count);}@end
Calculator *clac = [Calculator new]; int result = [clac process:2 withBlock:^int(int addend) { return addend + 1; }]; NSLog(@"%d",result);
塊的詞彙範圍
局部變數的聲明需要放在使用該局部變數的塊之前。預設情況下,塊常量運算式中不能對局部變數進行修改,使用__block
修改符可以將這些變數切換為讀寫入模式,但__block
修改符不能與auto、register和static組合使用。
int myVar = 10; void (^logValueBlock)(void) = ^{ NSLog(@"Variable value = %d", myVar); }; logValueBlock(); __block int myVar2 = 10; void(^intBlock)(int) = ^(int amount){ myVar2 += amount; NSLog(@"New value = %d", myVar2); }; intBlock(5);
塊的記憶體管理
在運行程式時,塊常量運算式會獲得棧記憶體,因而會擁有與局部變數相同的生命週期。因此它們必須被複製到永久儲存地區(即堆)中,才能定義它們的範圍之外使用。例如,如果你想要從方法獲得類型為塊常量的傳回值或者儲存塊常量,就必須將塊複製到堆中並在不再使用它們時釋放這些塊。
在MRR記憶體管理方式中,塊的copy和release需要達到平衡
void (^greetingBlock)(void); { greetingBlock = [^{ NSLog(@"Hello,World!"); } copy]; } greetingBlock(); [greetingBlock release]; // 釋放塊,防止記憶體流失
而在ARC記憶體管理方式中,編譯器會自動執行塊的複製和釋放操作
塊的使用
下面是兩個使用塊的例子:
使用塊為數組排序
#import <Foundation/Foundation.h>#include <stdlib.h>#define ArrayElements 10int main(int argc, const char * argv[]) { @autoreleasepool { // 建立一個含有隨機數值(0~99)的數組 NSMutableArray *numbers = [NSMutableArray arrayWithCapacity: ArrayElements]; for (int elem = 0; elem < ArrayElements; elem++) { unsigned int value = arc4random() % 100; [numbers addObject:[NSNumber numberWithUnsignedInt:value]]; } NSLog(@"Values:%@",numbers); // 記錄未排序的數值 // 以升序方式為數組數值排序 [numbers sortUsingComparator:^(id obj1, id obj2){ if ([obj1 integerValue] > [obj2 integerValue]) { return (NSComparisonResult)NSOrderedDescending; } if ([obj1 integerValue] < [obj2 integerValue]) { return (NSComparisonResult)NSOrderedAscending; } return (NSComparisonResult)NSOrderedSame; }]; NSLog(@"Values:%@",numbers); //記錄未排序的數值 } return 0;}
使用塊的並行編程方式
#import <Foundation/Foundation.h>#define YahooURL @"http://www.yahoo.com/index.html"#define ApressURL @"http://www.apress.com/index.html"typedef void (^DownloadURL)(void);// 擷取用於下載URL的塊DownloadURL getDownloadURL(NSString *url){ NSString *urlString = url; return ^{ // 下載URL NSDate *startTime = [NSDate date]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]; NSError *error; // 注意,NSURLConnection在iOS9中已被廢除,推薦使用NSURLSession,後面有NSURLSession的使用方法 NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error]; if (data == nil) { NSLog(@"Error loading request %@",[error localizedDescription]); } else{ NSDate *endTime = [NSDate date]; NSTimeInterval timeInterval = [endTime timeIntervalSinceDate:startTime]; NSLog(@"Time taken to download %@ = %f seconds", urlString, timeInterval); } };}int main(int argc, const char * argv[]) { @autoreleasepool { // 建立工作要求 dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 建立任務分組 dispatch_group_t group = dispatch_group_create(); // 擷取度量的目前時間 NSDate *startTime = [NSDate date]; // 建立並指派非同步任務 dispatch_group_async(group, queue1, getDownloadURL(YahooURL)); dispatch_group_async(group, queue2, getDownloadURL(ApressURL)); // 等待,直到分組中的所有任務完成為止 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 為並行操作和日誌檢索時間資訊 NSDate *endTime = [NSDate date]; NSTimeInterval timeInterval = [endTime timeIntervalSinceDate:startTime]; NSLog(@"Time taken to download URLs concurrently = %f seconds", timeInterval); } return 0;}
使用NSURLSession:
// NSURLSession在命令列程式中可能無法正常使用,可以在iOS環境下調試 NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"123"); if (data == nil) { NSLog(@"Error loading request %@",[error localizedDescription]); } else{ NSDate *endTime = [NSDate date]; NSTimeInterval timeInterval = [endTime timeIntervalSinceDate:startTime]; NSLog(@"Time taken to download %@ = %f seconds", urlString, timeInterval); } }]; [dataTask resume];
運行結果:
2016-07-14 16:03:50.780 BlockConcurrentTasks[20262:170183] Time taken to download http://www.apress.com/index.html = 1.259164 seconds2016-07-14 16:03:54.485 BlockConcurrentTasks[20262:170182] Time taken to download http://www.yahoo.com/index.html = 4.965020 seconds2016-07-14 16:03:54.486 BlockConcurrentTasks[20262:170152] Time taken to download URLs concurrently = 4.965577 seconds
可以看出並行方式執行任務的時間比以非同步方式所消耗的時間更少。
[精通Objective-C]塊(block)