Objective-C回调

1.回调机制

所谓回调就是讲一段可执行的代码与特定的一个事件绑定起来,当事件发生时就会调用这段代码。

Objective-C的回调有四种途径实现

  1. 目标-动作对(target-action): 事件发生时,向特定的对象发送特定的消息。接收消息的对象为目标,消息的选择器(selector)是动作。
  2. 辅助对象(helper objects):事件发生时,向遵守特定协议的辅助对象发送消息。委托对象(delegate)和数据源(data source)是常见的辅助对象。
  3. 通知(notification): 苹果公司添加了一种称之为通知中心(notification center)的对象。程序开始前,可以告知消息中心“某个对象正在等待某些特定的通知。当通知出现时,向指定的对象发送特定的消息”。即事件发生时,相关对象向通知中心发布通知,然后由通知中心将通知发生给等待通知的对象。
  4. Block对象(Blocks): Block是一段可执行代码,声明一个Block对象,在事件发生时,调用该Block对象。

事件驱动的程序需要一个等待事件发生的负责,OS X和iOS系统用NSRunloop的类(运行循环)来等待事件的发生,示例代码如下:

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
    @autoreleasepool{
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}
Runloop

runloop是一种闲时循环,等待事件的发生,runloop会有一个autorelease pool,runloop更新时[pool drain],向池中的对象发送release消息。

2.具体实现

(1)目标-动作对(target-action)

以NSTimer对象每隔2S,让一个TSLogger对象设置时间和打印时间。

//  TSLogger.h
#import <Foundation/Foundation.h>

@interface TSLogger : NSObject

@property (nonatomic) NSDate * lastTime;

-(NSString *)lastTimeString;
-(void)updateLastTime:(NSTimer *)t;

@end
//  TSLogger.h
#import "TSLogger.h"

@implementation TSLogger

-(NSString *)lastTimeString
{
    static NSDateFormatter *dateFormater = nil;
    if(!dateFormater)
    {
        dateFormater = [[NSDateFormatter alloc] init];
        [dateFormater setTimeStyle:NSDateFormatterMediumStyle];
        [dateFormater setDateStyle:NSDateFormatterMediumStyle];
        NSLog(@"create dateFormater");
    }
    return [dateFormater stringFromDate:self.lastTime];
}

-(void)updateLastTime:(NSTimer *)t
{
    NSDate *now = [NSDate date];
    [self setLastTime:now];
    NSLog(@"just set time to %@", self.lastTimeString);
    
}

@end

//  main.m
#import <Foundation/Foundation.h>
#import "TSLogger.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool{
        TSLogger *logger = [[TSLogger alloc] init];
        __unused NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                          target:logger
                                                       selector:@selector(updateLastTime:)
                                                        userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

先看main.m中在NSTimer对象timer中设置了目标-动作对的目标为TSLogger对象logger,动作为updateLastTime:,时间是每隔2S,重复进行。而在TSLogger对象中进行了具体的动作方法的实现。

这就是目标-动作对的大体用法,即在指定的时刻触发事件,情况比较简单,而且只做了一件事。

(2)辅助对象(helper objects)

辅助对象有委托对象数据源两种,我们先看委托对象

<1>委托对象

委托(delegate)就是将一件属于委托者做的事情交给被委托者来处理。受委托完成任务的对象称之为委托对象

创建一个委托协议;

对于委托者:

  1. 委托者声明要委托的属性,该属性遵守协议;
  2. 委托者在自己实现的方法通过遵守协议的属性,调用协议内的方法;
  3. 委托者设置好需要委托对象维护的属性

对于委托对象:

  1. 委托对象实现委托协议所定义的方法。

举例如下

有个人想要开公交赚钱,但是只有自己一个人忙不过来,需要找个售票的帮他卖票和告知情况。这里我们假定每2S来一个乘客。

  1. 创建一个Bus协议,描述需求是需要会卖票和报告卖票信息。对应第一步
  2. 在公交车类里声明一个卖票者属性,这个属性应由外界赋值。对应A.1
  3. 公交车上路后(startRun),代码内部的sellTicket由准守该协议的onePerson执行。对应A.2
//公交车的使用协议
@protocol BusProtocol <NSObject>

-(void)sellTicket;
-(void)reportSituation;
@end

//公交车类的声明
@interface Bus : NSObject

@property(nonatomic,strong)id<busProtocol>onePerson;
-(void)startRun;
@end

//公交车类的实现
@implementation Bus

-(void)startRun
{
        if(self.onePerson)
        {
            [self.onePerson sellTicket];
            [self.onePerson reportSituation];
        }
}

@end

我们需要的卖票对象如下,它遵守协议实现了卖票的方法。对应B.1

//委托对象seller类的声明
@interface Seller : NSObject <busProtocol>

@end

//委托对象seller类的实现
@implementation Seller

-(void)sellTicket
{
        NSLog(@"开始售票!");
}
-(void)reportSituation
{
        NSLog(@"完成售票!");
}
@end

最后在main中,设置委托者里需要委托对象维护的属性,即onePerson是委托对象seller。对应A.3

#import <Foundation/Foundation.h>
#import "Bus.h"
#import "Seller.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool{
        Bus * busone = [[Bus alloc] init];
        Seller * sell = [[Seller alloc] init];
        busone.onePerson = (id) sell;
        __unused NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0
                                                          target:busone
                                                        selector:@selector(startRun)
                                                        userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run];
        
    }
    return 0;
}

结果每来一个客人(每2S),Bus都会委托Seller对象进行售票和汇报。

2018-04-08 16:51:40.768918+0800 test[1612:289822] 开始售票!
2018-04-08 16:51:40.768946+0800 test[1612:289822] 完成售票!
2018-04-08 16:51:42.769788+0800 test[1612:289822] 开始售票!
2018-04-08 16:51:42.769820+0800 test[1612:289822] 完成售票!

委托可以向一个对象发送多个回调。

(3)通知(notification)

一个对象发生变化时,多个对象想要获得这个变化的通知,比如我们修改了系统的时区,很多对象会想要知晓这一变化。系统的时区发生变化时,会向通知中心发送NSSystemTimeZoneDidChangeNotification通知,然后通知中心会将该通知转发给相应的观察者们。

实例代码如下,将airlineHostess(空姐)和airlineBoy作为观察者,修改时区后,airlineHostess和airlineBoy广播换时区了,一个中文,一个英文。

//  airlineHostess.h
#import <Foundation/Foundation.h>

@interface airlineHostess : NSObject
-(void) report;
@end

//  airlineHostess.m
#import "airlineHostess.h"

@implementation airlineHostess
-(void) report
{
    NSLog(@"亲爱的乘客们,我们换时区啦!");
}
@end

main.m中注册观察者

#import <Foundation/Foundation.h>
#import "airlineHostess.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool{
        airlineHostess * beauty = [[airlineHostess alloc] init];
        [[NSNotificationCenter defaultCenter]
                                addObserver:beauty
                                selector:@selector(report)
                                name:NSSystemTimeZoneDidChangeNotification
                                object:nil];
        airlineBoy * handsome = [[airlineBoy alloc] init];
        [[NSNotificationCenter defaultCenter]
                                addObserver:handsome
                                selector:@selector(reportEnglish)
                                name:NSSystemTimeZoneDidChangeNotification
                                object:nil];
        [[NSRunLoop currentRunLoop] run];
    }
    
    return 0;
}

运行后,打开系统的时间与偏好设置修改时区,结果如下。

2018-04-08 19:18:29.488975+0800 test[1786:359257] 亲爱的乘客们,我们换时区啦!
2018-04-08 19:18:29.516014+0800 test[1786:359257] Dear passengers, we are in a new time zone!

(4)Block对象(Blocks)

Block知识点较多,将放入下一篇文章讲解

block对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容