自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Swift 項目-Xib之StoryBoard 多人協作技巧

開發(fā) 前端
說到底,臃腫的Storyboard和臃腫的ViewController一樣,都是難以維護且容易git沖突的。唯一的解決方案就是有節(jié)制的使用工具。

不同于國外,StoryBoard從面世到如今飽受國內開發(fā)者的質疑,質疑的理由很多,什么不利于多人協作啊,隱藏了UI細節(jié)啊,出問題不容易測試,降低執(zhí)行效率啊等等。此文就是針對這些問題的舉例和剖析。

StoryBoard 和 Xib 有什么區(qū)別?

StoryBoard 和 Xib 都是用來分離UI樣式代碼,改善視圖代碼重用率,增加所見即所得,降低視圖測試繁復度的視圖系列化工具。

  • 其中Xib以視圖View為主。
  • StoryBoard 和 Xib 有什么區(qū)別。
  • StoryBoard 以控制器Controller及其之間的關系,以及和視圖View的關系為主。

StoryBoard 和 Xib 不利于多人協作,git合并代碼容易沖突,且難以處理?

這個是詆毀StoryBoard最多的理由,也是看上去最充分的理由。最顯著的就是下圖這種失敗的例子。

Storyboard不利圖片

在一個Storyboard中,大量的Controller控制器和Segue連線彰顯著錯綜復雜的UI關系,使人望而生畏或者難以維護。

但這并不應該是Storyboard的鍋,僅僅是使用者對工具的濫用!

沒錯,就是濫用,無論是Storyboard也好,純代碼也罷,它們的本質都是工具,工具本身沒有正義或邪惡,影響工具的是使用者。哪怕是用純代碼開發(fā),如果沒有命名規(guī)范,肆意的嵌套if,不遵守MVC或者MVVM等開發(fā)模式,不區(qū)分開發(fā)環(huán)境與生產環(huán)境,這樣寫出來的代碼又何談可維護性,和多人協作呢?

那么反過來說,如何使用Storyboard才不算濫用?

避免濫用,最好的方法就是定制規(guī)范,就好像代碼中的諸多規(guī)范一樣。每個團隊可能有自己不同的喜好,我在此拋磚引玉,列出我們團隊使用Storyboard的規(guī)范,供大家參考。

每個模塊獨立Storyboard每個Storyboard只應該有一個主VC和同頁的子VC,主VC不應存在2個以上。

  1. 一個項目中,Storyboard不該是孤立存在的,應該像MVP模式那樣,每個頁面都有獨立的Storyboard,每個Storyboard只應該有一個主VC和同頁的子VC,主VC不應存在2個以上。(絕大多數情況下,一個Storyboard上只應該有一個VC)。
  2. 頁面間的Segue連線應該使用Stroyboard Reference Scene,UITabBarController的子頁因為復雜度應該當成主VC處置
  3. 視圖的初始樣式應盡量在Storyboard上屬性面板中設置,非極特殊情況,布局也應在Storyboard上使用各種約束配合完成。這樣有利于視圖樣式和視圖代碼分離,有利于視圖代碼重用性和兼容性提高。
  4. 對于邏輯復雜的VC,應添加Object對象,并綁定相應的類來分離邏輯代碼。
  5. 對于圓角,背景色,陰影等CALayer的樣式,應該使用擴展或子類化實例的形式,使用@IBInspectable屬性關鍵字,在Storyboard屬性面板中設定初始樣式。
  6. 對于自定義視圖,應使用@IBDesignable關鍵字保障在在Storyboard上所見即所得!

使用以上原則,只要任務分工合理,基本上不存在多人同時修改同一個Storyboard的情況,就算配合失誤偶然發(fā)生,精簡的Storyboard其代碼量也不大,借助文件比較工具很容易就能處理git沖突。

說到底,臃腫的Storyboard和臃腫的ViewController一樣,都是難以維護且容易git沖突的。唯一的解決方案就是有節(jié)制的使用工具。

StoryBoard 和 Xib 隱藏了UI細節(jié),且容易導致ViewController臃腫?

與其說StoryBoard 和 Xib 隱藏了UI細節(jié),倒不如說蘋果是希望通過他們來引導開發(fā)者正確的使用視圖和控制器 ,他們創(chuàng)建視圖實例的時候都是通過

    required init?(coder aDecoder: NSCoder) {

}

構造方法創(chuàng)建視圖實例。所有初始樣式都是在屬性面板中設置的值,通過

    func setValue(_ value: Any?, forUndefinedKey key: String) {
......
}

來賦值給視圖對應的屬性。

至于說導致ViewController臃腫,更是荒謬,StoryBoard提供了多種方案來分離代碼,只不過很多人不知道而已。

拿美團的主頁UI舉例:

這樣的首頁較為復雜,正常布局的話需要多個CollectionView和一個UITableView;

如果這些視圖的Delegate都由ViewController來實現,自然顯得臃腫且混亂。

一般手寫派會分出3個ChildViewController來解決臃腫問題,難道Storyboard就做不到么?

答案是否定的,很早的版本,蘋果就給出了上圖中的解決方案。一個占位的容器視圖指向子控制器的Embed Segue;

按住Control鍵連線到想要包含的子控制器,占位視圖的實例==子控制器的view(子控制器根視圖);

選擇Embed連線方式后,子控制器 的尺寸變化成跟占位視圖一樣的尺寸;

這樣我們可以將功能圖標的CollectionView的代碼放到這第一個子控制器上,CollectionViewDelegate、CollectionViewDataSource等代碼也由子控制器實現;

同理,優(yōu)惠專區(qū)可以再添加一個Container View,指向第二個子控制器。

通過 Container View 創(chuàng)建的ChildViewController如何與主ViewController傳參或互相調用?

ChildViewController 可以通過 self.parent(Swift)|| self.parentViewController(OC)來拿到主ViewController的實例。主ViewController可以通過 self.chilren(Swift) || self.childViewControllers(OC)來拿到ChildViewController的實例,它是一個數組,順序等同于占位視圖再視圖層次中的順序。

值得一提的是,通過此種方式創(chuàng)建的ChildViewController,其構造方法晚于主ViewController,但生命周期中的viewDidLoad則早于主ViewController, 因此在ChildViewController中的viewDidLoad方法中,self.parent 是nil,這時不能拿到主ViewController實例。如果需要在初始化的時候拿到主ViewController的實例,則應該在主ViewController``viewDidLoad方法中,調用ChildViewController的特定方法,把self 當參數傳過去。

  • 除此之外還可以使用Object對象。

將它添加到控制器之上。

它的本質是一個繼承自NSObject的子類,我們完全可以把它當成一個小功能模塊的控制器。

class FeaturesController: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {

@IBOutlet weak var collectionView:UICollectionView!

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
<#code#>
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
<#code#>
}
}

在Storyboard上選中這個Object,綁定上面的類:

右鍵這個Object,在彈出的菜單中連線:

右鍵CollectionView 設置 Delegate 和 DataSource 等的連線:

在主ViewController中如需調用這個模塊的方法或者傳參:

class HomeController: UIViewController {

@IBOutlet weak var featuresController:FeaturesController!

override func viewDidLoad() {
super.viewDidLoad()

featuresController.datas = [....]
featuresController.collectionView.reloadData()
}

}

完成連線,同理,如果一個頁面需要多個子模塊,可以在Storyboard上拖入多個Object,并綁定不同的模塊控制類,相對于占位的Container View和ChildViewController方法,Object方法在傳參或互相調用方面,更加簡便。缺點是沒有ChildViewController的生命周期方法,如需使用viewWillAppear等,需要在主ViewController的viewWillAppear中,調用Object的自定義方法。

通過上面的2種方法不難看出,并非是Storyboard造成ViewController代碼臃腫,而是因為設計不當導致,就算你不用Storyboard,把所有功能都寫在一個ViewController里一樣臃腫。這都是使用者決定的,并非Storyboard的責任!

StoryBoard 和 Xib 出了問題不容易測試?

這個問題其實問的很模糊,我也是咨詢了很多人才知道,他們所謂的問題不容易測試,是指如下兩種情況:

  1. 修改或刪除 @IBOutlet 的變量名時,對應的Storyboard上未做處理,導致運行時崩潰,崩潰內容看不懂!
  2. 綁定的類名改變時,對應的Storyboard上未做處理,導致運行時崩潰,崩潰內容看不懂!

其實只要知道,蘋果是如何把Storyboard的xml解析成視圖,崩潰的錯誤內容也就容易看懂了 之前提到過,視圖構造使用的是下面這個方法:

 required init?(coder aDecoder: NSCoder) {

}

如果綁定的類名改變輸出錯誤:

  1. Unknown class _TtC11ProjectName14HomeController in Interface Builder file. // Swift
  2. Unknown class HomeController in Interface Builder file. // Objective C

通過上面的錯誤提示Interface Builder file就是指通過Storyboard或者Xib構建視圖或者控制器,但找不到名為HomeController的控制器,看到這里就應該明白,我們某個Storyboard上綁定了名為HomeController的控制器,但代碼中找不到,可能是改名或者刪除了。這時可以全局搜素一下:

在搜出來的結果中可以看到,是在Main.storyboard上綁定了HomeController,Test.swift文件中定義了該類,但是因為改名所以無法找到。

這樣的問題不用Storyboard就可以避免么?答案是否定的,因為重構代碼的時候,改了一處忽略它處的例子比比皆是。哪怕純代碼也是一樣,因此,如果需要修改類名或者變量名,應該善用Xcode的重構功能,而不是簡單的直接修改。

這樣修改類名或者變量名是,Storyboard或者Xib上綁定或連線的內容也會同步改變。就不會出錯了。

同理,@IBOutlet 連線的屬性通過下面的方法給視圖賦值。

 func setValue(_ value: Any?, forUndefinedKey key: String) {
......
}

如果變量名改變的時候,會出現如下錯誤:

  1. *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key featuresController.'

這個方法找不到對應的屬性時,就會拋出異常, 這里就是指找不到featuresController屬性,通過全局搜索可以發(fā)現,代碼中改了名字。

解決的方法同樣是刪掉對應的連線或者修改變量名時使用重構。

由此可見,所謂的不容易測試,完全是因為重構不謹慎且對構造過程不理解,否則還是很容易定位問題且修改的。而且重構代碼時利用Xcode重構功能的話,連問題都不會出現。

StoryBoard 和 Xib 降低執(zhí)行效率?

這個問題看起來好像是那么回事,StoryBoard 和 Xib本質上是XML,要解析成視圖就需要反序列化,必然沒有直接代碼創(chuàng)建速度高,但這只是感覺上,實際上有多少影響呢?我們來測試一下:

     var controllers:[ViewController] = []
let count = 30000
controllers.reserveCapacity(count)
guard let sb = storyboard else { return }

var beginTime = CACurrentMediaTime()
for _ in 0..<count {
let vc = sb.instantiateViewController(withIdentifier: "ViewController") as! ViewController

controllers.append(vc)
}
print("Storyboard創(chuàng)建(count)次用時", CACurrentMediaTime() - beginTime)
controllers.removeAll(keepingCapacity: true)
beginTime = CACurrentMediaTime()
for _ in 0..<count {

let vc = ViewController()

controllers.append(vc)
}
print("純代碼創(chuàng)建(count)次用時", CACurrentMediaTime() - beginTime)

第一次使用了3萬次,結果輸出:

  1. Storyboard創(chuàng)建30000次用時 8.648092089919373
  2. 純代碼創(chuàng)建30000次用時 27.226440161000937

我們看到了什么?從Storyboard創(chuàng)建竟然比純代碼更快?簡直不敢相信自己的眼睛,而且差距這么大一定是有什么神奇的事情發(fā)生,為了驗證我的想法,我又將Storyboard創(chuàng)建復制了一次:

var controllers:[ViewController] = []
let count = 30000
controllers.reserveCapacity(count)
guard let sb = storyboard else { return }

var beginTime = CACurrentMediaTime()
for _ in 0..<count {
let vc = sb.instantiateViewController(withIdentifier: "ViewController") as! ViewController

controllers.append(vc)
}
print("Storyboard創(chuàng)建(count)次用時", CACurrentMediaTime() - beginTime)

controllers = []
controllers.reserveCapacity(count)

beginTime = CACurrentMediaTime()
for _ in 0..<count {

let vc = ViewController()

controllers.append(vc)
}
print("純代碼創(chuàng)建(count)次用時", CACurrentMediaTime() - beginTime)

controllers = []
controllers.reserveCapacity(count)

beginTime = CACurrentMediaTime()
for _ in 0..<count {
let vc = sb.instantiateViewController(withIdentifier: "ViewController") as! ViewController

controllers.append(vc)
}
print("Storyboard創(chuàng)建(count)次用時", CACurrentMediaTime() - beginTime)

輸出結果如下,而且多次運行結果相近,可能是因為隨著內存使用率提高,電腦性能在降低,影響了結論,但不管怎么說,大量測試空的ViewController在這種情況下確實比純代碼創(chuàng)建更快。

  1. Storyboard創(chuàng)建30000次用時 8.513293381780386
  2. 純代碼創(chuàng)建30000次用時 27.19225306995213
  3. Storyboard創(chuàng)建30000次用時 25.9916725079529

這個結果是如何出現的,不妨大膽猜測一下,可能是由于蘋果在對象多次創(chuàng)建的情況下,Storyboard可能存在緩存復刻機制,來提升效率,而純代碼并沒有這樣的優(yōu)化。為了驗證猜測,我們逐漸降低數量級。

1.Storyboard創(chuàng)建3000次用時 0.20833597797900438

2.純代碼創(chuàng)建3000次用時 0.2654381438624114

3.Storyboard創(chuàng)建3000次用時 0.34943647705949843

1.Storyboard創(chuàng)建300次用時 0.010981905972585082

2.純代碼創(chuàng)建300次用時 0.005475352052599192

3.Storyboard創(chuàng)建300次用時 0.014193600043654442

1.Storyboard創(chuàng)建30次用時 0.0016030301339924335

2.純代碼創(chuàng)建30次用時 0.00031192018650472164

3.Storyboard創(chuàng)建30次用時 0.001034758985042572

1.Storyboard創(chuàng)建10次用時 0.0009886820334941149

2.純代碼創(chuàng)建10次用時 0.0001325791236013174

3.Storyboard創(chuàng)建10次用時 0.0014422889798879623

上述結果果然驗證了我們的猜測,隨著次數的減少,Storyboard創(chuàng)建的速度逐漸低于存代碼創(chuàng)建,但單次耗時仍然低于萬分之一秒,這種效率是不會讓用戶有任何感知的,何況重復創(chuàng)建比純代碼還有優(yōu)勢,因此,這一條也不算StoryBoard 和 Xib的缺點。

在 StoryBoard 和 Xib 拖動和設置約束布局很難精確?不易修改?

我想,這種言論可能是因為不太熟悉Interface Builder的功能和操作造成的,僅僅實驗了幾次不得其門而入就放棄了。

實際上約束布局是一個很強大的功能,可以解決絕大多數(98%)布局適配問題,98%這個數并不是隨便給出的,很多人覺得達不到這個比例是因為對約束理解較少,還是按照以前的autolayoutMask的方式使用約束,因此很多布局問題還在用代碼計算,可實際上約束功能十分強大,目前無法通過約束直接解決,必須代碼輔助的問題微乎其微。

但與之相對的是約束的概念較多,依賴人腦思考很容易產生遺漏,這樣在運行的時候就會各種報錯或顯示異常,因此用純代碼寫約束,反復運行調試視圖樣式尺寸十分常見,而且有些頁面較深,測試起來十分麻煩。

而使用StoryBoard 或 Xib就不同了,缺少約束或者約束沖突直接就有錯誤提示,適配不同設備可以直接在Interface Builder上切換測試,效率不知高了多少倍,準確性也高了很多。

總結,StoryBoard 和 Xib雖然不是毫無缺點,但優(yōu)勢遠大于付出,值得學習研究!

參考資料:

[1]https://juejin.cn/post/6844903763526811655: https://juejin.cn/post/6844903763526811655

[2]https://juejin.cn/post/6844903763526811655: https://juejin.cn/post/6844903763526811655[3]

https://juejin.cn/post/6844903763526811655: https://juejin.cn/post/6844903763526811655

責任編輯:武曉燕 來源: Swift社區(qū)
相關推薦

2021-03-28 17:21:15

Git分支策略

2021-09-15 14:53:35

在線文檔多人協作

2011-11-30 16:37:58

sync

2022-02-23 15:29:24

SwiftSwift 之父Chris

2016-11-10 07:50:55

群暉云端Office

2009-07-14 08:26:46

微軟網絡版Office網絡版Office功能Office 2010

2021-09-08 15:43:03

在線寫作協作文檔辦公軟件

2015-02-04 10:32:57

Objective-CSwift

2015-10-23 13:25:12

Github團隊協作Pull

2024-04-11 10:20:00

谷歌AI

2015-07-30 10:42:38

SwiftUIButton

2015-08-03 11:45:37

storyboard

2011-07-25 15:56:45

組策略

2013-08-29 09:37:18

GitHub開源項目

2021-12-08 10:31:46

ITIT領導IT管理

2021-08-13 14:35:37

視頻會議軟件遠程工作

2011-07-01 16:05:22

SEO

2011-08-22 10:14:56

Xib文件iPoneiPad

2014-06-05 14:12:05

SwiftUI學習iOS

2015-04-17 16:30:46

swiftOC
點贊
收藏

51CTO技術棧公眾號