Objective-C回調(diào)

1.回調(diào)機制

所謂回調(diào)就是講一段可執(zhí)行的代碼與特定的一個事件綁定起來,當(dāng)事件發(fā)生時就會調(diào)用這段代碼。

Objective-C的回調(diào)有四種途徑實現(xiàn)

  1. 目標(biāo)-動作對(target-action): 事件發(fā)生時,向特定的對象發(fā)送特定的消息。接收消息的對象為目標(biāo),消息的選擇器(selector)是動作。
  2. 輔助對象(helper objects):事件發(fā)生時,向遵守特定協(xié)議的輔助對象發(fā)送消息。委托對象(delegate)和數(shù)據(jù)源(data source)是常見的輔助對象。
  3. 通知(notification): 蘋果公司添加了一種稱之為通知中心(notification center)的對象。程序開始前,可以告知消息中心“某個對象正在等待某些特定的通知。當(dāng)通知出現(xiàn)時,向指定的對象發(fā)送特定的消息”。即事件發(fā)生時,相關(guān)對象向通知中心發(fā)布通知,然后由通知中心將通知發(fā)生給等待通知的對象。
  4. Block對象(Blocks): Block是一段可執(zhí)行代碼,聲明一個Block對象,在事件發(fā)生時,調(diào)用該Block對象。

事件驅(qū)動的程序需要一個等待事件發(fā)生的負責(zé),OS X和iOS系統(tǒng)用NSRunloop的類(運行循環(huán))來等待事件的發(fā)生,示例代碼如下:

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

runloop是一種閑時循環(huán),等待事件的發(fā)生,runloop會有一個autorelease pool,runloop更新時[pool drain],向池中的對象發(fā)送release消息。

2.具體實現(xiàn)

(1)目標(biāo)-動作對(target-action)

以NSTimer對象每隔2S,讓一個TSLogger對象設(shè)置時間和打印時間。

//  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中設(shè)置了目標(biāo)-動作對的目標(biāo)為TSLogger對象logger,動作為updateLastTime:,時間是每隔2S,重復(fù)進行。而在TSLogger對象中進行了具體的動作方法的實現(xiàn)。

這就是目標(biāo)-動作對的大體用法,即在指定的時刻觸發(fā)事件,情況比較簡單,而且只做了一件事。

(2)輔助對象(helper objects)

輔助對象有委托對象數(shù)據(jù)源兩種,我們先看委托對象

<1>委托對象

委托(delegate)就是將一件屬于委托者做的事情交給被委托者來處理。受委托完成任務(wù)的對象稱之為委托對象

創(chuàng)建一個委托協(xié)議;

對于委托者:

  1. 委托者聲明要委托的屬性,該屬性遵守協(xié)議;
  2. 委托者在自己實現(xiàn)的方法通過遵守協(xié)議的屬性,調(diào)用協(xié)議內(nèi)的方法;
  3. 委托者設(shè)置好需要委托對象維護的屬性

對于委托對象:

  1. 委托對象實現(xiàn)委托協(xié)議所定義的方法。

舉例如下

有個人想要開公交賺錢,但是只有自己一個人忙不過來,需要找個售票的幫他賣票和告知情況。這里我們假定每2S來一個乘客。

  1. 創(chuàng)建一個Bus協(xié)議,描述需求是需要會賣票和報告賣票信息。對應(yīng)第一步
  2. 在公交車類里聲明一個賣票者屬性,這個屬性應(yīng)由外界賦值。對應(yīng)A.1
  3. 公交車上路后(startRun),代碼內(nèi)部的sellTicket由準(zhǔn)守該協(xié)議的onePerson執(zhí)行。對應(yīng)A.2
//公交車的使用協(xié)議
@protocol BusProtocol <NSObject>

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

//公交車類的聲明
@interface Bus : NSObject

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

//公交車類的實現(xiàn)
@implementation Bus

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

@end

我們需要的賣票對象如下,它遵守協(xié)議實現(xiàn)了賣票的方法。對應(yīng)B.1

//委托對象seller類的聲明
@interface Seller : NSObject <busProtocol>

@end

//委托對象seller類的實現(xiàn)
@implementation Seller

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

最后在main中,設(shè)置委托者里需要委托對象維護的屬性,即onePerson是委托對象seller。對應(yīng)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;
}

結(jié)果每來一個客人(每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] 完成售票!

委托可以向一個對象發(fā)送多個回調(diào)。

(3)通知(notification)

一個對象發(fā)生變化時,多個對象想要獲得這個變化的通知,比如我們修改了系統(tǒng)的時區(qū),很多對象會想要知曉這一變化。系統(tǒng)的時區(qū)發(fā)生變化時,會向通知中心發(fā)送NSSystemTimeZoneDidChangeNotification通知,然后通知中心會將該通知轉(zhuǎn)發(fā)給相應(yīng)的觀察者們。

實例代碼如下,將airlineHostess(空姐)和airlineBoy作為觀察者,修改時區(qū)后,airlineHostess和airlineBoy廣播換時區(qū)了,一個中文,一個英文。

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

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

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

@implementation airlineHostess
-(void) report
{
    NSLog(@"親愛的乘客們,我們換時區(qū)啦!");
}
@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;
}

運行后,打開系統(tǒng)的時間與偏好設(shè)置修改時區(qū),結(jié)果如下。

2018-04-08 19:18:29.488975+0800 test[1786:359257] 親愛的乘客們,我們換時區(qū)啦!
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對象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容