在iOS應(yīng)用中的行為——by Krzysztof Zab?ocki
作為開發(fā)者前域,我們力求編寫整潔且組織良好的代碼。達(dá)到這個(gè)目的我們有很多模式可以使用封孙,其中最好的一個(gè)當(dāng)屬組合(composition)模式了迹冤。組合更容易讓我們遵循單一職責(zé)原則(Single Responsibility Principe
)并簡化類文件。
不像重視圖控制器(Massive View Controller)同時(shí)服務(wù)于不同的角色(如數(shù)據(jù)源和委托)那樣敛瓷,你可以把這些角色分離到不同的類文件中叁巨。視圖控制器只負(fù)責(zé)配置它們并協(xié)調(diào)工作斑匪。畢竟呐籽,代碼寫的越少,需要調(diào)試和維護(hù)的代碼也就越少蚀瘸。
那么行為究竟是什么狡蝶?
行為是一個(gè)負(fù)責(zé)實(shí)現(xiàn)特定角色的對象,例如贮勃,你可以有一個(gè)實(shí)現(xiàn)視差動畫的行為贪惹。
這篇文章中的行為將利用Interface Builder
限制代碼量,同時(shí)與非編碼人員進(jìn)行有效的合作寂嘉。然而奏瞬,如果你不使用Interface Builder
你也可以使用行為,仍然可以獲取大多數(shù)的好處泉孩。
很多行為除了設(shè)置之外不需要任何額外的代碼硼端,你完全可以通過Interface Builder
和代碼(同樣的設(shè)置方法)完成這些設(shè)置。很多情況下寓搬,你甚至不需要用屬性引用它們珍昨。
為什么使用行為?
許多iOS項(xiàng)目最后都成了重視圖控制器(Massive View Controller)類句喷,因?yàn)槿藗儼褢?yīng)用中80%的邏輯放到了這里镣典。這是一個(gè)嚴(yán)重的問題,因?yàn)樵噲D控制器是我們代碼中復(fù)用率最少的部分唾琼,很難對他們進(jìn)行測試和維護(hù)兄春。
行為可以幫我們避免這種情形,那么它給我們帶來了那些好處呢锡溯?
輕視圖控制器(Lighter View Controller)
使用行為意味著可以吧很多代碼從控制器中移入到分離的類文件中赶舆。如果使用行為肴裙,通常都會形成輕量的視圖控制器。例如涌乳,我的通常少于100行代碼蜻懦。
代碼復(fù)用
因?yàn)樾袨閮H僅只負(fù)責(zé)一個(gè)角色,它很容易避免特定行為和特定應(yīng)用之間邏輯的依賴夕晓。這允許你在不同的應(yīng)用之間共享香味宛乃。
測試性
行為是像黑盒一樣工作的小類。這意味著他們很容易被單元測試覆蓋蒸辆。你甚至不用創(chuàng)建真實(shí)的視圖只用提供模擬對象就可以測試它們征炼。
讓非編碼人員修改應(yīng)用邏輯的能力
如果我們決定使用Interface Builder
利用行為,我們可以教設(shè)計(jì)師如何修改應(yīng)用邏輯躬贡。設(shè)計(jì)師可以添加谆奥、移除行為和修改參數(shù),并不需要知道Objective-C的任何相關(guān)知識拂玻。
這對工作流來說是很大的一個(gè)好處酸些,特別是小團(tuán)隊(duì)。
如何創(chuàng)建靈活的行為檐蚜?
行為是一個(gè)不需要任何專門代碼的簡單對象魄懂,但是這有幾個(gè)概念可以真正的幫助它們更易使用,更加強(qiáng)大闯第。
運(yùn)行時(shí)屬性
很多開發(fā)者都忽視了Interface Builder
市栗,甚至都沒有學(xué)習(xí)過它,同樣的咳短,經(jīng)常會錯(cuò)過它真正的強(qiáng)大之處填帽。
運(yùn)行時(shí)屬性是使用Interface Builder
的關(guān)鍵特性之一。它們給你提供一種設(shè)置自定義類的方式咙好,設(shè)置可以為iOS的內(nèi)建類設(shè)置屬性篡腌。例如,你是否設(shè)置過圖層的圓角半徑敷扫?你可以直接在Interface Builder
中為其簡單地設(shè)置運(yùn)行時(shí)屬性:
當(dāng)在
Interface Builder
中創(chuàng)建行為的時(shí)候哀蘑,你打算嚴(yán)重依賴運(yùn)行時(shí)屬性去設(shè)置行為選項(xiàng)。結(jié)果葵第,這兒通常會有更多的運(yùn)行時(shí)屬性:行為的生存時(shí)間
如果一個(gè)對象從Interface Builder
創(chuàng)建绘迁,它會立即創(chuàng)建立即移除,除非其他的對象擁有它的強(qiáng)引用卒密。然而缀台,這對行為來說并不是完美的,因?yàn)樗枰饔糜诘囊晥D控制器存活的時(shí)間一樣長哮奇。
你可以在視圖控制器中創(chuàng)建一個(gè)屬性并強(qiáng)引用行為膛腐,但是這并不完美睛约,原因有如下幾個(gè):
- 當(dāng)創(chuàng)建并配置好行為后,你不需要同很多行為進(jìn)行交互哲身。
- 創(chuàng)建屬性僅僅只為了保持對象生存這很麻煩辩涝。
- 如果需要移除特定的行為,你需要去清理那些未使用的屬性勘天。
使用Objective-C運(yùn)行時(shí)去反轉(zhuǎn)生存時(shí)間綁定
而不是手動的在視圖控制器中設(shè)置行為的強(qiáng)引用怔揩,如果需要的話,我們可以把行為當(dāng)做視圖的一個(gè)關(guān)聯(lián)對象脯丝,作為配置過程的一部分商膊。
這意味著,在某種情況下如果我們需要移除特定的行為宠进,我們只需要移除配置這個(gè)行為的代碼或者Interface Builder
對象晕拆,并不需要額外的改變。
這可以像下面那樣實(shí)現(xiàn):
@interface KZBehavior: UIControl
@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 *), nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
這里我們利用關(guān)聯(lián)對象來創(chuàng)建特定所有者對象的強(qiáng)引用材蹬。
行為事件
讓行為能夠發(fā)布事件非常有用实幕,例如,當(dāng)一個(gè)動畫結(jié)束時(shí)赚导〔缢酰可以通過從UIControl
繼承行為來在Interface Builder
中啟用該功能赤惊。然后吼旧,特定的行為可以調(diào)用:
[self sendActionsForControlEvents:UIControlEventValueChanged];
這允許你把行為連接到視圖控制器的代碼上。
一個(gè)簡單行為的例子
因此未舟,什么東西最容易作為行為來實(shí)現(xiàn)圈暗?
下面是將視差動畫添加到UIViewController類(不是自定義類)的簡單方法:
視頻
是否曾經(jīng)從你的照片庫和相機(jī)選擇一張圖片?
視頻
更為高級的特性
上面的行為很簡單裕膀,但是员串,當(dāng)我們需要更高級的特性的時(shí)候,以是否香菇應(yīng)該怎么做昼扛?行為可以做到你想讓他有多強(qiáng)大它就有多強(qiáng)大寸齐,我們看幾個(gè)更為復(fù)雜的例子。
如果行為需要幾個(gè)像UIScrollViewDelegate這類委托的話抄谐,你很快就會看到在一個(gè)特定的界面上你最多只能有一個(gè)行為的情況渺鹦。但是,我們可以通過實(shí)現(xiàn)一個(gè)簡單地多路代理的對象來處理這種問題:
@interface MutiplexerProxyBehavior: KZBehavior
@property(nonatomic, strong) IBOutletCollection(id) NSArray *targets;
@end
@implementation MutiplexerProxyBehavior
- (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)建一個(gè)多路行為的實(shí)例蛹含,你可以把它指定為滾動視圖的一個(gè)委托(或者任何一個(gè)有委托的對象)毅厚,這樣委托調(diào)用將會轉(zhuǎn)發(fā)給所有人。
結(jié)論
行為是一個(gè)有趣的概念浦箱,它可以簡化你的代碼庫吸耿,允許你在不同的應(yīng)用間復(fù)用很多代碼祠锣。它們還將允許你通過微調(diào)或者修改應(yīng)用中的行為,從而同團(tuán)隊(duì)中的非編碼人員有效的協(xié)作咽安。