最近對做IOS 項目遇到回調,抽空把相關資料整理下,以下是整理內容:
回調
回調就是將一段可執行檔代碼和一個特定的事件綁定起來。當特定的事件發生時,就會執行這段代碼。
在Objective-C中,有四條途徑可以實現回調。
目標-動作對
在程式開始定等待前,要求“當時間發生時,向指定的對象發送某個特定的資訊”。這裡接收訊息的對象是目標,訊息的選取器是動作。
輔助對象
在程式開始等待之前,要求“當時間發生時,向遵守相應協議的輔助對象發送訊息”。委派物件和資料來源是常見的輔助對象。
通知
蘋果公司提供了一種稱為通知中樞的對象。在程式開始等待前,就可以告知通知中樞”某個對象正在等待某些特定的通知。當其中的某個通知出現時,向指定的對象發送特定的訊息”。當事件發生時,相關的對象會向通知中樞發布通知,然後再由通知中樞將通知轉寄給正在等待通知的對象。
Block對象
Block是一段可執行代碼。在程式開始等待前,聲明一個Block對象,當事件發生時,執行這段Block對象。
NSRunLoop
iOS中有一個NSRunLoop類,NSRunLoop執行個體會持續等待著,當特定的事件發生時,就會向相應的對象發送訊息。NSRunLoop執行個體會在特定的事件發生時觸發回調。
迴圈
實現回調之前要先建立一個迴圈:
int main(int argc, const char * argv[]) { @autoreleasepool { [[NSRunLoop currentRunLoop]run]; } return 0;}
目標-動作對
建立一個擁有NSRunLoop對象和NSTimer對象的應用程式。每隔兩秒,NSTimer對象會向其目標發送指定的動作訊息,建立一個新的類,名為BNRLogger,為NSTimer對象的目標。
在BNRLogger.h中聲明動作方法:
#import <Foundation/Foundation.h>@interface BNRLogger : NSObject<NSURLSessionDataDelegate>@property(nonatomic) NSDate *lastTime;-(NSString *) lastTimeString;-(void)updateLastTime: (NSTimer *) t;@end
在BNRLogger.m中實現方法:
#import "BNRLogger.h"@implementation BNRLogger-(NSString *)lastTimeString{ static NSDateFormatter *dateFormatter=nil; if(!dateFormatter) { dateFormatter =[[NSDateFormatter alloc]init]; [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; NSLog(@"created dateFormatter"); } return [dateFormatter stringFromDate:self.lastTime];}-(void)updateLastTime:(NSTimer *)t{ NSDate *now=[NSDate date]; [self setLastTime:now]; NSLog(@"Just set time to %@",self.lastTimeString);}@end
main.m中建立一個BNRLogger執行個體:
#import <Foundation/Foundation.h>#import "BNRLogger.h"int main(int argc, const char * argv[]) { @autoreleasepool { BNRLogger *logger=[[BNRLogger alloc]init]; __unused NSTimer *timer=[NSTimer scheduledTimerWithTimeInterval:2.0 target:logger selector:@selector(updateLastTime:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop]run]; } return 0;}
輔助對象
我的上一篇Blog已經寫過NSURLSession方法的使用,那麼輔助對象回調的使用,將BNRLogger對象成為NSURLSession的委派物件,特定的事件發生時,該對象會向輔助對象發送訊息。
main.m中建立一個NSURL對象以及NSURLRequest對象。然後建立一個NSURLSession對象,設定BNRLogger的執行個體為它的
委派物件:
#import <Foundation/Foundation.h>#import "BNRLogger.h"int main(int argc, const char * argv[]) { @autoreleasepool { BNRLogger *logger=[[BNRLogger alloc]init]; //URL是一張圖片的下載連結 NSURL *url = [NSURL URLWithString:@"yun_qi_img/error.html&thumburl=http%3A%2F%2Fimg4.imgtn.bdimg.com%2Fit%2Fu%3D2349180720%2C2436282788%26fm%3D11%26gp%3D0.jpg"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; __unused NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:logger delegateQueue:[NSOperationQueue mainQueue]]; __unused NSTimer *timer=[NSTimer scheduledTimerWithTimeInterval:2.0 target:logger selector:@selector(updateLastTime:) userInfo:nil repeats:YES]; //4.根據會話對象建立一個Task(發送請求) NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request]; //5.執行任務 [dataTask resume]; [[NSRunLoop currentRunLoop]run]; } return 0;}
BNRLogger.h中,聲明NSURLSessionDataDelegate協議:
#import <Foundation/Foundation.h>@interface BNRLogger : NSObject<NSURLSessionDataDelegate>@property (nonatomic, strong) NSMutableData *responseData;@property(nonatomic) NSDate *lastTime;-(NSString *) lastTimeString;-(void)updateLastTime: (NSTimer *) t;@end
BNRLogger.m中,有NSURLSession的代理方法,具體可以看NSURLSession:
#import "BNRLogger.h"@implementation BNRLogger-(NSMutableData *)responseData{ if (_responseData == nil) { _responseData = [NSMutableData data]; } return _responseData;}-(NSString *)lastTimeString{ static NSDateFormatter *dateFormatter=nil; if(!dateFormatter) { dateFormatter =[[NSDateFormatter alloc]init]; [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; [dateFormatter setDateStyle:NSDateFormatterMediumStyle]; NSLog(@"created dateFormatter"); } return [dateFormatter stringFromDate:self.lastTime];}-(void)updateLastTime:(NSTimer *)t{ NSDate *now=[NSDate date]; [self setLastTime:now]; NSLog(@"Just set time to %@",self.lastTimeString);}//1.接收到伺服器響應的時候調用該方法-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{ //在該方法中可以得到回應標頭資訊,即response NSLog(@"didReceiveResponse--%@",[NSThread currentThread]); NSLog(@"響應"); //注意:需要使用completionHandler回調告訴系統應該如何處理伺服器返回的資料 //預設是取消的 /* NSURLSessionResponseCancel = 0, 預設的處理方式,取消 NSURLSessionResponseAllow = 1, 接收伺服器返回的資料 NSURLSessionResponseBecomeDownload = 2,變成一個下載請求 NSURLSessionResponseBecomeStream 變成一個流 */ completionHandler(NSURLSessionResponseAllow);}//2.接收到伺服器返回資料的時候會調用該方法,如果資料較大那麼該方法可能會調用多次-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{ NSLog(@"didReceiveData--%@",[NSThread currentThread]); NSLog(@"返回"); //拼接伺服器返回的資料 [self.responseData appendData:data];}//3.當請求完成(成功|失敗)的時候會調用該方法,如果請求失敗,則error有值-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ NSLog(@"didCompleteWithError--%@",[NSThread currentThread]); NSLog(@"完成"); if(error == nil) { //解析資料 NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:nil]; NSLog(@"%@",dict); }}@end
通知
當系統時區發生變化時,會向通知中樞發布NSSystemTimeZoneDidChangeNotification通知,然後通知中樞會將該通知轉寄給相應的觀察者。
main.m中將BNRLogger執行個體註冊為觀察者,系統時區設定發生變化可以收到相應的通知:
//在”輔助對象”方法應用程式中的main.m中加入這行代碼 [[NSNotificationCenter defaultCenter]addObserver:logger selector:@selector(zoneChange:) name:NSSystemTimeZoneDidChangeNotification object:nil];
在BNRLogger.m中實現該方法:
//在”輔助對象”方法應用程式中的BNRLogger.m中加入這行代碼 -(void)zoneChange:(NSNotification *)note{ NSLog(@"The system time zone has changed!");}
Block回調
把上面所講的“通知”方法應用程式main.m中的:
[[NSNotificationCenter defaultCenter]addObserver:logger selector:@selector(zoneChange:) name:NSSystemTimeZoneDidChangeNotification object:nil];
改為:
[[NSNotificationCenter defaultCenter]addObserverForName:NSSystemTimeZoneDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ NSLog(@"The system time zone has changed!"); }];
“通知”方法應用程式BNRLogger.m中的這個方法去掉:
-(void)zoneChange:(NSNotification *)note{ NSLog(@"The system time zone has changed!");}
總結
- 對於只做一件事情的對象(例如),使用目標-動作對。
- 對於功能更複雜的對象(例如NSURLSession),使用輔助對象。最常見的輔助物件類型是委派物件。
- 對於要觸發多個(其他對象中的)回調的對象(例如NSTimeZone),使用通知。
- Block實現回調使代碼便於閱讀。
感謝閱讀,希望能協助到大家,謝謝大家對本站的支援!