你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> Block實現iOS回調

Block實現iOS回調

編輯:IOS開發綜合

回調函數是我們在編程中經常使用到的,但是很多新手只知道怎麼用,不知其所以然。今天我們就來剖析下回調函數到底是個什麼鬼。

先來看一個關於回調函數的形象的比喻

你到一個商店買東西,剛好你要的東西沒有貨,於是你在店員那裡留下了你的電話,過了幾天店裡有貨了,店員就打了你的電話,然後你接到電話後就到店裡去取了貨。在這個例子裡,你的電話號碼就叫回調函數,你把電話留給店員就叫登記回調函數,店裡後來有貨了叫做觸發了回調關聯的事件,店員給你打電話叫做調用回調函數,你到店裡去取貨叫做響應回調事件

再看下比較正式的定義

callback(回調)就是一段「代碼」,我們會通過某種途徑,將這段「代碼」和一個特定的事件(event)聯系起來,當特定事件(event)發生後,這段「代碼」被執行。


好了,大家先不管能不能理解上面兩種定義,先在腦袋裡面有個印象。我們下面來通過實際的例子幫助大家理解回調函數的使用,然後在回頭看看上面的定義,定會有撥開烏雲見天日的感覺。

為什麼要有「回調(callback)」

在這裡,斗膽將程序分為兩種:

「非事件驅動」型程序「事件驅動(event-driven)」型程序

「非事件驅動」型程序。

這類程序,遵循這樣一個流程:啟動程序 -> 執行程序(代碼) -> 退出程序。程序會在執行完所有代碼後,立刻退出,中途不會有任何事件(event)發生(除非有bug)。

「事件驅動(event-driven)」型程序

這類程序,遵循這樣一個流程:啟動程序 -> 等待事件(event) -> 事件被觸發 -> 執行callback(回調) -> 繼續等待事件(event) -> 人為退出程序。

打個比方,我想用淘寶APP幫手機充值,一打開APP,它並不會馬上跳到充值頁面,是要等待我的點擊事件,當點擊了充值的按鈕,才會跳到充值頁面(執行了callback)。

所以,大家應該很容易聯想到,iOS的應用幾乎都是「事件驅動(event-driven)」的,應用一經啟動,就在等待事件的發生,當發生某個事件(比如點擊了某個按鈕),應用就會執行某段代碼(callback)進行響應。

這裡的「事件(event)」,是非常寬泛的,可以是使用者的一次點擊、可以是系統的一次通知、可以是服務器返回的一次數據、可以是藍牙外設連接成功後,發送給手機的一條指令等等。


通過上面我們知道在事件驅動型的程序裡面,我們需要當某一個特定條件被觸發的時候去調用回調函數來執行某個動作。

下面我們來看看具體的例子。

實例1、數據更新觸發回調

如下圖所示:

在個人主頁我們需要顯示用戶的昵稱、頭像、性別、簽名,然後點擊編輯按鈕,跳轉到編輯個人信息頁面,編輯完個人信息之後,點擊返回到主頁的時候,我們需要根據用戶設定的值來更新相應的信息。

個人主頁
編輯用戶信息
實現代碼如下:

personalHomePage.m文件
**************************
- (void)editPersonalInfo{
    EditPersonalInfoController *edit = [[SGEditPersonalInfoController alloc]init];

    __weak typeof(self)weakself = self;
    edit.updateUserInfoBlock = ^(NSString *userName, NSString *userSex, NSString *userSign ,UIImage *userImage)
    {
        weakself.userHeaderImageView.image = [userImage circleImage];//用戶頭像
        weakself.nickNameLabel.text = userName;//昵稱
        weakself.signLabel.text = userSign;//簽名
        weakself.sexImageView.image = ([userSex isEqualToString:@"女"]) ? [UIImage imageNamed:@"icon_female"] : [UIImage imageNamed:@"icon_male"];//性別

     }
}
EditPersonalInfoController.h文件
***********************************
@interface EditPersonalInfoController :UITableViewController
@property(nonatomic, copy)void(^updateUserInfoBlock)(NSString *userName, NSString *userSex, NSString *userSign ,UIImage *userImage) ;
@end


EditPersonalInfoController.m文件
***********************************
- (void)personalInfoHadChanged{
    if (self.updateUserInfoBlock) {
        self.updateUserInfoBlock(self.userName, self.userSex, self.userSign, self.userImage);
    }
}

為了方便敘述,我們做如下規定:
personalHomePage類為A,EditPersonalInfoController為B

1、首先我們在A頁面的editPersonalInfo方法裡面跳轉到B頁面,在這個方法裡面我們登記回調函數(block),這個回調函數的作用就是更新A頁面的信息。

2、首先B頁面需要申明回調函數,就是@property(nonatomic, copy)void(^updateUserInfoBlock)(NSString *userName, NSString *userSex, NSString *userSign ,UIImage *userImage) ;,然後B頁面在個人信息被更改,這個時候觸發了回調關聯的事件,調用方法personalInfoHadChanged方法,在方法裡面來調用回調函數,參數就是自己頁面更改的信息。

3、一旦回調函數被調用,A頁面就響應回調事件,作用就是根據B頁面傳遞過來的參數更改A頁面的各個控件的值。

    edit.updateUserInfoBlock = ^(NSString *userName, NSString *userSex, NSString *userSign ,UIImage *userImage)
    {
        weakself.userHeaderImageView.image = [userImage circleImage];//用戶頭像
        weakself.nickNameLabel.text = userName;//昵稱
        weakself.signLabel.text = userSign;//簽名
        weakself.sexImageView.image = ([userSex isEqualToString:@"女"]) ? [UIImage imageNamed:@"icon_female"] : [UIImage imageNamed:@"icon_male"];//性別

     }

現在再回頭把我在這三個步驟裡面標記的紅色字體部分和文章開頭的比喻對照看下,是不是感覺有些明白了。我們再接再厲,多看幾個例子,加深理解。

實例2、人為觸發回調

除了實例1中提到的當特定的數據(頁面數據,服務器返回數據等)被更改的時候需要觸發回調函數做一些事情,還有一種情況也需要觸發回調函數來做一些處理。

比如用戶點擊了某個按鈕或者頁面,我們需要做回調去處理一些邏輯。

具體來看一個實際場景:

如下圖所示,是當網絡無連接的時候顯示的一個view,提示用戶可以點擊頁面進行重新加載。

網絡錯誤View

\
image

我們需要給該view添加一個Tap手勢,當用戶點擊的時候我們就觸發回調函數,讓使用該viwe的控制器去重新加載數據。

代碼實現:

NetworkErrorPromptView.h文件
**********************************
@interface NetworkErrorPromptView : UIView
@property(copy,nonatomic)void(^reloadBlock)();//申明回調函數
@end


NetworkErrorPromptView.m文件
**********************************
@implementation NetworkErrorPromptView

-(instancetype)initWithFrame:(CGRect)frame{
    if (self == [super initWithFrame:frame]) {
        self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil] firstObject];
        [self addGestureRecognizer:[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(reload)]];
    }
    return self;
}

-(void)reload{
    if (self.reloadBlock)
    {
        self.reloadBlock();//調用回調函數
    }
}
@end

假設在一個UITableViewController裡面使用了該view,使用懶加載初始化該view

-(NetworkErrorPromptView*)errorView{
    if (!_errorView) {
        _errorView = [[SGNetworkErrorPromptView alloc]init];
        _errorView.frame = CGRectMake(0, 0, self.tableView.w, self.tableView.h);
        __weak typeof(self) weakself = self;
        _errorView.reloadBlock = ^{//登記回調函數,被觸發就調用
            [weakself.tableView.mj_header beginRefreshing];
        };
    }
    return _errorView;
}

PS:
實例1和實例2非常類似,只是觸發回調函數的方式不一樣而已,大家理解了實例1,實例2應該不難理解。
所以我就不再一步步的講解,只是在使用和觸發回調函數的地方做了注釋


iOS的四種回調模式

其實在iOS裡面有四種回調模式,具體如下圖所示:

我們上面講解的是使用block實現回調,因為block的語法看起來有些怪異,所以很多人剛開始使用block實現回調函數就容易出錯。

其實block我們可以理解為簡版的delegate模式,delegate模式我相信大家理解起來不困難吧,我們經常使用UITableViewController就有很多代理方法。

代理模式就是A頁面在特定狀態(我們上面說的實例1和2)委托B頁面去做某一件事(觸發回調函數)。

仔細想一下是不是就是block啊。

如果你不熟悉block的使用,完全可以使用delegate來代替block,用多了就理解回調函數了,然後你想用delegate還是block亦或是notification都可以。(notification少用,影響性能)


總結

我們知道如何使用回調函數,那麼我們什麼時候才需要用到回調函數呢?

結合上面的實例1和2,我們不難發現,以下場景需要使用回調函數。

如果我們的代碼邏輯就是從上到下按順序執行,中途不需要去做其他的邏輯,那就沒必要使用回調函數了。

如果我們需要讓只有當某一特定條件滿足的時候(數據被更新,用戶點擊事件),我們才會去執行一段邏輯。這個時候我們就需要使用回調函數來實現了。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved