鍍金池/ 教程/ iOS/ iOS 中的行為
與四軸無人機的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

iOS 中的行為

作為一個開發(fā)者,我們致力于編寫簡潔并且良好架構(gòu)的代碼。很多設(shè)計模式都可以實現(xiàn)這一點,其中最好的一種是組合模式。組合模式使編寫出的代碼更易于遵循功能單一原則并且可以使我們的類簡化。

為了替代一個功能上服務(wù)于不同模塊(就像 data sources 和 delegates )的繁冗的視圖控制器,我們將這些模塊劃分到不同的類中。這個視圖控制器就可可以僅僅負(fù)責(zé)這些類的配置和協(xié)調(diào)它們工作。畢竟,代碼寫的越少,調(diào)試和維護代碼的工作量就越少。

那么,行為究竟到底是個什么東西呢?

行為是一個負(fù)責(zé)實現(xiàn)一個指定功能的對象,比如你可以有一個實現(xiàn)視差動畫的行為。

在這篇文章中所描述的各種行為將可以通過使用 Interface Builder 從而減少大量的代碼書寫,同時使與非編碼人員的協(xié)同工作更高效。然而,就算你不使用 Interface Builder,你也能從中獲益頗多。

許多行為只需要進行設(shè)置,而不再需要寫額外的代碼,而對這些行為的配置可以通過 Interface Builder 或者(相同設(shè)置方法的)代碼完整地實現(xiàn)。在大多數(shù)情況下,你不必再去用額外的屬性引用它們。

為何使用行為?

大多數(shù)的 iOS 工程中都有繁冗的視圖控制器類,因為大家都習(xí)慣于將 80% 的應(yīng)用邏輯寫在這里。這卻是一個很嚴(yán)肅的問題,因為在我們的代碼中視圖控制器這部分的代碼復(fù)用是最少的,并且很難對它們(代碼)進行測試與維護。

這里描述的行為幫助我們避免這種場景,那這樣做可以給我們帶來什么好處呢?

更輕量級的視圖控制器

使用行為模式意味著將更多的代碼從視圖控制器中剝離到其他相應(yīng)的類中。如果你堅持使用這種行為模式,你將最終實現(xiàn)一個非常輕量級的視圖控制器。舉個例子,我所寫的視圖控制器一般不超過 100 行代碼。

代碼復(fù)用

由于行為的職責(zé)單一,所以它可以非常容易地實現(xiàn)與特定行為和特定應(yīng)用邏輯的解耦。這使你可以在不同的應(yīng)用中使用相同的行為代碼。

可測試性

行為都是一些功能簡單的類,其工作原理像一個黑盒。這意味單元測試很容易的完整的覆蓋這部分邏輯。你可以不需要創(chuàng)建真實的視圖,而只需提供模擬對象就能對它們進行測試,

使非程序員能夠改變應(yīng)用邏輯

如果我們決定通過 Interface Builder 來使用行為,我們就可以教會我們的設(shè)計師如何去更改應(yīng)用邏輯。設(shè)計師可以增刪行為并且修改參數(shù),而并不需要理解任何 Objective-C 的相關(guān)內(nèi)容。

這對工作流程有著巨大的好處,針對小團隊來說尤為明顯。

如何構(gòu)建一個復(fù)雜的行為

行為是一些簡單的對象并且不需要太多定制的代碼,但是這里有一些概念可以真正的幫助行為更易使用,并且使用起來更具威力。

運行時屬性

許多開發(fā)者輕視了 Interface Builder 甚至從來不去學(xué)習(xí)它,正因如此,他們通常不了解 Interface Builder 功能到底有多強大。

運行時屬性是 Interface Builder 使用中最關(guān)鍵的特性之一。它們?yōu)槟闾峁┝艘粋€構(gòu)建自定義類甚至設(shè)置 iOS 內(nèi)置類的屬性的途徑。舉個例子,你是否曾為你的層設(shè)置圓角呢?你其實可以直接在 IB 中進行簡單的運行時屬性配置來實現(xiàn)這一點。

http://wiki.jikexueyuan.com/project/objc/images/13-10.png" alt="" />

當(dāng)在 Interface Builder 中創(chuàng)建行為時,你將重度依賴運行時屬性去設(shè)置行為選項。最終將會帶來更多的典型運行時屬性:

http://wiki.jikexueyuan.com/project/objc/images/13-11.png" alt="" />

行為的生命周期

如果一個對象是被 Interface Builder 所創(chuàng)建出來的,除非另外的一個對象對其強引用,否則它將會在被創(chuàng)建后立刻移除。這點對于需要一直在視圖控制器上工作的行為來講并不理想,這樣的行為會希望和視圖控制器擁有同樣的生命周期。

我們可以嘗試在視圖控制器上創(chuàng)建一個對行為強引用的屬性,但這同樣也不完美,有如下的理由:

  • 對于許多行為來說,你可能并不需要在創(chuàng)建并配置之后與其進行交互。
  • 只是為了保持一個對象活躍而創(chuàng)建一個屬性,怎么說都很悲劇。
  • 如果你想要移除一個指定的行為,你需要去清除那個無用了的屬性。

使用 Objective-C 運行時解綁生命周期

不要選擇在視圖控制器上設(shè)置強引用來手動綁定一個行為,取而代之,如果有需要的話,我們可以在配置過程中將行為自己賦值為視圖控制器的關(guān)聯(lián)對象 (associated object) 。

這意味著,如果我們需要移除一個指定的行為,我們只需要移除對應(yīng)的配置行為的代碼或者 Interface Builder 對象就可以了,而無需任何額外的改變。

實現(xiàn)如下:

@interface KZBehavior : UIControl

//! object that this controller life will be bound to
@property(nonatomic, weak) IBOutlet id owner;

@end

@implementation KZBehavior

- (void)setOwner:(id)owner
{
    if (_owner != owner) {
        [self releaseLifetimeFromObject:_owner];
        _owner = owner;
        [self bindLifetimeToObject:_owner];
    }
}

- (void)bindLifetimeToObject:(id)object
{
    objc_setAssociatedObject(object, (__bridge void *)self, self,   OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)releaseLifetimeFromObject:(id)object
{
    objc_setAssociatedObject(object, (__bridge void *)self, nil,    OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

在這里我們使得已關(guān)聯(lián)的對象對一個指定所有者的對象構(gòu)建了一個強引用。

行為事件

行為可以發(fā)送事件,這個特性非常有用,比如當(dāng)一個動畫完成時我們就能收到通知。我們可以在 IB 里面通過構(gòu)建繼承于 UIControl 的行為類來啟用這個特性。一個特性的行為可以調(diào)用:

[self sendActionsForControlEvents:UIControlEventValueChanged];

這將允許你將行為關(guān)聯(lián)到你的視圖控制器里面的代碼。

基礎(chǔ)行為的例子

那么,什么樣的事情用行為實現(xiàn)會最簡單呢?

這里我們將展示在一個 UIViewController 類(不是自定義的類)中添加一個視差動畫是一件多么容易的事情:

或者需要從你的相冊或者相機中獲取一張圖片?

進階特性

上述的行為都很簡單粗暴,但是,你是否也想知道當(dāng)我們需要更多進階特性的時候需要做什么?只要肯做,行為的功能可以非常強大,讓我們來看看一些更復(fù)雜的例子。

如果你的行為需要一個代理,如 UIScrollViewDelegate ,你將會很快陷入一個困境,你無法在一個特定屏幕上擁有多余一個的行為。但是,我們可以通過實現(xiàn)一個簡單多路代理(遍歷 NSInvocation 命令數(shù)組進行匹配,譯者注)對象來解決這個問題。

@interface MultiplexerProxyBehavior : KZBehavior

//! targets to propagate messages to
@property(nonatomic, strong) IBOutletCollection(id) NSArray *targets;

@end

@implementation MultiplexerProxyBehavior

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    NSMethodSignature *sig = [super methodSignatureForSelector:sel];
    if (!sig) {
        for (id obj in self.targets) {
            if ((sig = [obj methodSignatureForSelector:sel])) {
                break;
            }
        }
    }
    return sig;
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    BOOL base = [super respondsToSelector:aSelector];
    if (base) {
        return base;
    }

    return [self.targets.firstObject respondsToSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    for (id obj in self.targets) {
        if ([obj respondsToSelector:anInvocation.selector]) {
            [anInvocation invokeWithTarget:obj];
        }
    }
}

@end

通過創(chuàng)建一個多路代理的實例,你可以把它作為一個 scroll view (或者其他有代理的對象) 的代理,然后將代理的調(diào)用轉(zhuǎn)發(fā)給所有的行為對象。

總結(jié)

行為是一個非常有趣的概念,它可以簡化你的代碼庫,并可以允許在不同的應(yīng)用間實現(xiàn)許多代碼復(fù)用。它們(行為類)將會引領(lǐng)你在團隊中與非編碼人員進行更有效率的工作,并允許他們對應(yīng)用的行為進行調(diào)整和修改。