IOS之回调方法总结

什么是回调

回调(callback)就是将一段可执行的代码和一个特定的事件绑定起来,当特定的事情被触发的时候,就会执行这段代码,这就是回调。IOS里面回调包括四种:目标-动作对(target-action)、辅助对象(helper objects)、通知(notification)以及Block对象(Blocks);

四种回调

  • 目标-动作对(target-action): 在程序开始等待之前,要求“当事件发生时,向指定的对像发送某个特定的消息”,这里接受消息的对象是目标(target),消息的选择器(selector)是动作(action);
  • 辅助对象(Helper Objects): 在程序开始等待前,要求“当事件发生时,向遵守相应协议的辅助对象发送消息”。Delegate(委托对象) 和 DataSource(数据源)是我们常见的辅助对象;
  • 通知(Notification): 某个对象正在等待某些特定的通知。当其中的某个通知出现时,向指定的对象发送特定的消息。当事件发生时,相关的对象会向通知中心发布通知,然后再有通知中心将通知转发给正在等待该通知的对象;
  • Block对象(Blocks): 在程序开始等待前,声明一个Block对象,当事件发生时,执行这段Block对象;

循环对象

在将案例之前让我们先来了解一下循环对象(下文有用上),IOS有个循环类叫NSRunLoop,这个实例一直会持续等待着,当有特定的事件发生的时候,就会给相应的对象发送对应的消息,这个实例会在特定事件发生时出发回调,类似Android的handler消息机制,用法如下:

[[NSRunLoop currentRunLoop] run];

目标-动作对

首先先创建一个Logger类,代码如下:
Logger.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Logger : NSObject

@property (nonatomic) NSDate *LastTime;
-(NSString *)LastTimeString;
-(void)UpdateLastTime:(NSTimer *)t;

@end

NS_ASSUME_NONNULL_END

然后在Logger.m文件中实现对应的方法:

#import "Logger.h"

@implementation Logger

-(NSString *)LastTimeString{
    static NSDateFormatter *dateFormatter = nil;
    if (!dateFormatter) {
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
        NSLog(@"create 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文件中,创建一个Logger实例,让它成为NSTimer的目标,把动作设置为updateLa stTime,mian文件如下:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Logger.h"
#import <Foundation/Foundation.h>

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
                
       Logger *logger = [[Logger alloc] init];
        
        //这里用了@selector语句来传递动作消息的名称给相应的方法,要传递实参,不能只传递方法名
        __unused NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:logger selector:@selector(UpdateLastTime:) userInfo:nil repeats:YES];
        
        [[NSRunLoop currentRunLoop] run]}
    
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

运行结果如下所示:
在这里插入图片描述
每隔2秒就会像对应的目标发送指定动作的消息,当收到回调之后就会打印出相关日志;

辅助对象

辅助对象的话我们用一个异步机制来讲解。

我们使用NSURLConnection从服务器获取数据时,通常都是通过异步方式完成的,NSURLConnection通常不会一次就发送全部数据,而是多次的发送块状数据。也就是说,我们需要在程序中不断的响应接受数据的事件。因此,我们需要一个对象来帮助NSURLConnection完成这些操作。

因为要完成NSURLConnection的操作,所以Logger当中要实现它的协议,在这个简单的例子中,我们只需要实现NSURLConnection的三个协议方法就好,辅助对象可以根据协议实现相应的方法,以下是该协议所声明的部分方法(在main中创建一个NSURL和NSURLRequest对象,然后再创建一个NSConnection对象,把Logger设置为它的委托对象):

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Logger.h"
#import <Foundation/Foundation.h>

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        
        Logger *logger = [[Logger alloc] init];
        
        [[NSNotificationCenter defaultCenter] addObserver:logger selector:@selector(zoneChanhe) name:NSSystemTimeZoneDidChangeNotification object:nil]
        
        NSURL *url = [NSURL URLWithString:@"https://www.gutenberg.org/cache/epub/205/pg205.txt"];
        
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        __unused NSURLConnection *fetchConn = [[NSURLConnection alloc] initWithRequest:request delegate:logger startImmediately:YES];
        
        [[NSRunLoop currentRunLoop] run];

            
    }
    
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

然后再Logger中实现响应特定事件的回调方法,Logger.h:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Logger : NSObject

<NSURLConnectionDelegate,NSURLConnectionDataDelegate>

{
    //Data用来保存数据,当一块块数据发送过来的时候,将它们添加到NSMutableData中;
    NSMutableData *_incomingDate;
}
//将logger设置为了NSURLConnection的辅助对象,因此网络下载相关的信息都会在辅助对象logger中进行响应
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
-(void)connectionDidFinishLoading:(NSURLConnection *)connection;
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

@end

NS_ASSUME_NONNULL_END

Logger.m

#import "Logger.h"

@implementation Logger

//收到一定的字节数的时候会被调用
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    NSLog(@"received %lu bytes", [data length]);
    //如果NSMutableData不存在则创建一个
    if(!_incomingDate){
        _incomingDate = [[NSMutableData alloc] init];
    }
    
    [_incomingDate appendData:data];
}

//最后的数据处理完成之后会被调用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSLog(@"got it all!");
    //将NSData数据以utf-8解码为字符串
    NSString *string = [[NSString alloc] initWithData:_incomingDate encoding:NSUTF8StringEncoding];
    
    _incomingDate = nil;
    NSLog(@"string has %lu characters", [string length]);

}

//获取数据失败的时候会被调用
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    NSLog(@"connectiong failed : %@",[error localizedDescription]);
}

-(void)zoneChange:(NSNotification *)note{
    NSLog(@"the system time zone has change!!!");
}

@end

构建程序并运行,程序会陆续收到来自web服务器的数据。当数据全部接受完成以后,也会收到对应的委托消息;

在这里插入图片描述

当要向一个对象发送一个回调的时候,选择使用目标-动作对来实现;当要向一个对象发送多个消息的时候,选择辅助对象来实现;

通知

当改变系统的时区设置时,程序中的很多对象都可以知道这一变化。之所以能够实现,是因为这些对象都可以通过通知中心将自己注册成为观察者Observer。当系统时区发生改变的时候,会像通知中心发布NSSystemTimeZondeDidChangeNotification通知,然后通知中心将该通知转发给所有注册了该Name的观察者。

类似,我们将logger注册为观察者,当有人改变时区的时候,该观察者就会收到对应的通知:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Logger.h"
#import <Foundation/Foundation.h>

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        
        Logger *logger = [[Logger alloc] init];
        
        [[NSNotificationCenter defaultCenter] addObserver:logger selector:@selector(zoneChanhe) name:NSSystemTimeZoneDidChangeNotification object:nil]
                
        [[NSRunLoop currentRunLoop] run];

            
    }
    
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

在Logger.m里面实现如下方法:

-(void)zoneChange:(NSNotification *)note{
    NSLog(@"the system time zone has change!!!");
}

当Mac的时区发生改变的时候,便会收到对应的通知;

  • 通知中心不拥有观察者,如果将某个对象注册为观察者,那么通常应该在释放该对象的时候将其移出通知中心;
  • 对象不拥有委托对象或数据源对象,如果某个新创建的对象是另一个对象的委托对象或数据源对象,那么该对象应该在其dealloc方法中取消相对应的关联;
  • 对象不拥有目标,如果某个新创建的对象是另一个对象的目标,那么该对象应该在其dealloc方法中将其目标指针置为nil;

Block

Block看上去和c类似,都是在一个花括号内的一套指令,但是它没有函数名,对应的只有一个 ^ 符号,^表示这段代码是一个block对象,block对象可以有返回值;

^(int a,int b){
	int c = a + b;
	return c;
}

后续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值