作為一個開發(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 行代碼。
由于行為的職責(zé)單一,所以它可以非常容易地實現(xiàn)與特定行為和特定應(yīng)用邏輯的解耦。這使你可以在不同的應(yīng)用中使用相同的行為代碼。
行為都是一些功能簡單的類,其工作原理像一個黑盒。這意味單元測試很容易的完整的覆蓋這部分邏輯。你可以不需要創(chuàng)建真實的視圖,而只需提供模擬對象就能對它們進行測試,
如果我們決定通過 Interface Builder 來使用行為,我們就可以教會我們的設(shè)計師如何去更改應(yīng)用邏輯。設(shè)計師可以增刪行為并且修改參數(shù),而并不需要理解任何 Objective-C 的相關(guān)內(nèi)容。
這對工作流程有著巨大的好處,針對小團隊來說尤為明顯。
行為是一些簡單的對象并且不需要太多定制的代碼,但是這里有一些概念可以真正的幫助行為更易使用,并且使用起來更具威力。
許多開發(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)建一個對行為強引用的屬性,但這同樣也不完美,有如下的理由:
不要選擇在視圖控制器上設(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)到你的視圖控制器里面的代碼。
那么,什么樣的事情用行為實現(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ā)給所有的行為對象。
行為是一個非常有趣的概念,它可以簡化你的代碼庫,并可以允許在不同的應(yīng)用間實現(xiàn)許多代碼復(fù)用。它們(行為類)將會引領(lǐng)你在團隊中與非編碼人員進行更有效率的工作,并允許他們對應(yīng)用的行為進行調(diào)整和修改。