歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

關(guān)于Objective-C過渡到Swift的幾點(diǎn)建議

發(fā)布時(shí)間:2016-06-15 23:24  回復(fù):0  查看:3045   最后回復(fù):2016-06-15 23:24  

我們通過什么途徑Objective-C編程語言過渡到Swift語言呢?

 

1.從簡單的部分入手

 

我們決定從盡可能簡單的部分入手,例如一些獨(dú)立的可以自測和內(nèi)部調(diào)用的類。我們最初選擇的部分是簡單的UI控制代碼、實(shí)用函數(shù)以及已有類的擴(kuò)展方法。

 

例如在最初加入的Swift組件中,我們寫了一個(gè)String的擴(kuò)展方法來使本地化字符串讀起來更舒服:

 

extension String {

   var localized: String! {

      let localizedString = NSLocalizedString(self, comment: "")

      return localizedString

   }

}

 

有趣的是我們本可以用Objective-C的分類來實(shí)現(xiàn)相同功能。然而我們團(tuán)隊(duì)再也不會(huì)去使用陳舊的NSLocalizedString(@"MyText", @"")。許多新的思路隨著運(yùn)用一門新語言而浮出水面。所以從使用Swift開發(fā)的第一天開始我們的Swift字符串都是寫成"MyText".localized格式。

 

2.Swift中調(diào)用已有的Objective-C代碼

 

在寫了一些獨(dú)立的Swift組件并對(duì)它們進(jìn)行單元測試后,我們開始用Swift調(diào)用已有的Objective-C的類。一切都開始變得實(shí)際起來。

 

要從Swift調(diào)用Objective-C的類你需要定義Swift橋接頭文件。這是一個(gè).h文件,用來定義所有能暴露給Swift調(diào)用的Objective-C頭文件。在文件的開頭,需要修改編譯設(shè)置來讓編譯器在編譯的時(shí)候把它加進(jìn)去。當(dāng)這一切完成后,這些Objective-C的類就被導(dǎo)入進(jìn)Swift的世界,能被Swift很方便地調(diào)用。

 

當(dāng)你在Swift中調(diào)用Objective-C類時(shí),你可能會(huì)收到一個(gè)警告:pointer is missing a nullability type specifier。當(dāng)Objective-C代碼被導(dǎo)入到Swift,編譯器會(huì)檢查nullability compatibility--如果沒有任何關(guān)于nullability的信息,這個(gè)提示就會(huì)出現(xiàn)。要做這個(gè)檢查是因?yàn)樵?/span>Swiftnullability信息都是顯式申明的,不論是非空類型還是可選類型。

 

我們唯一需要做的僅僅是在被調(diào)用的Objective C頭文件中加入nullability信息來消除這些編譯器警告。我們使用新的nullablenonnull標(biāo)注來達(dá)成這一目的。重置工作僅僅只需要花幾個(gè)小時(shí),因?yàn)槲覀冎恍枰薷奈覀冊(cè)?/span>Swift中用到的類,無非是幾百行的代碼。然而,做這些修改時(shí)我們絞盡腦汁思考在已有代碼庫中哪些可以或不能被設(shè)置成nil,但當(dāng)我們把這些代碼暴露給Swift時(shí)我們無法避免的要做出一個(gè)明確的抉擇。

 

在大部分情況下,重構(gòu)涉及到一些像這樣的修改:

 

// .h文件中原來的方法簽名

@property (nonatomic, strong, readonly) THSession *session;

  

// 新的,Swift友好型的方法簽名

@property (nonatomic, strong, readonly, nullable) THSession *session;

 

在方法簽名中存在block時(shí),修改會(huì)略微復(fù)雜,但是沒有不可掌控的:

 

// .h文件中原來的方法簽名

- (NSURLSessionDataTask *)updateWithSuccess: (void(^)())success error:( void(^)(NSError * error))error;

  

// 新的,Swift友好型的方法簽名

- (nonnull NSURLSessionDataTask *)updateWithSuccess: (nullable void(^)())success error:(nullable void(^)(nonnull NSError *error))error;

 

3.Objective C調(diào)用Swift代碼

 

在有不少復(fù)雜度適中的使用了Objective-C類的Swift組件后,是時(shí)候在Objective C里面調(diào)用這些組件了。從Objective-C中調(diào)用Swift組件更直接,因?yàn)椴恍枰~外的橋接頭文件。

 

唯一需要對(duì)現(xiàn)有Swift文件做修改的是繼承NSObject或給我們希望暴露的Swift類添加@objc屬性。有一些特殊的SwiftObjective-C無法使用,像結(jié)構(gòu)體(structures),元組(tuples),和泛型(generics),以及一些其他的類。這些限制不會(huì)造成什么影響,因?yàn)槲覀儾幌氚讶魏涡碌慕Y(jié)構(gòu)暴露給Objective-C。唯一我們需要做些額外處理的特例是枚舉類型。要使用Swift中定義的枚舉類型,需要在Swift中明確申明Int值類型:

 

@objc enum FlightCabinClass: Int {

   case Economy = 1, PremiumEconomy, Business, First, PrivateJet

}

 

4.Swift角度重拾單元測試和依賴注入

 

當(dāng)我們有更多復(fù)雜的模塊使用了依賴時(shí),我們遇到了一個(gè)沒有明確解決方案的問題:單元測試。不像Objective-C,Swift不支持讀寫反射。簡單來說,Swift中沒有與OCMock等價(jià)的庫,事實(shí)上mocking框架并不存在。

 

這里有一個(gè)令我們抓狂的例子。我們希望測試當(dāng)我們?cè)谝粋€(gè)頁面點(diǎn)擊提交按鈕時(shí),saveTrip方法能被view對(duì)象的viewModel屬性調(diào)用。在Objective-C中,使用OCMock,測試起來非常輕松:

 

// 測試當(dāng)點(diǎn)擊了提交按鈕,ViewModel上的一個(gè)方法被調(diào)用

- (void)test_whenPressingSubmitButton_thenInvokesSaveTripOnViewModel {

   // given

   TripViewController *view = [TripViewController alloc] init];

   id viewModelMock = OCMPartialMock(view.viewModel);

   OCMExpect([viewModelMock saveTrip]);

  

   // when

   [view.submitButton press];

  

   // then

   OCMVerifyAll(viewModelMock);

}

 

Swift中這個(gè)方法不起作用。Objective-C單元測試常依賴于像OCMock這樣完善的模擬框架。依賴注入是一個(gè)很好的實(shí)踐,但因?yàn)?span>OCMock讓單元測試變得非常簡單,甚至不需要顯式的依賴注入,我們大部分的Objective-C依賴是隱式的。而在Swift中,像OCMock這樣的動(dòng)態(tài)模擬庫并不存在。在Swift中唯一可以寫出能測試代碼的方式是讓依賴變得顯式和可注入。一旦這個(gè)解決后,你需要自己寫模擬對(duì)象去驗(yàn)證行為。

 

重新審視前一個(gè)例子:在Swift中需要做修改,讓viewModel可以作為view的依賴被傳遞。只要viewModel實(shí)現(xiàn)了協(xié)議,或者繼承viewModel就能實(shí)現(xiàn)。測試類需要定義用來傳遞的模擬對(duì)象:

 

func test_whenPressingSubmitButton_thenInvokesSaveTripOnViewModel() {

   // given

   let viewModelMock = TripViewModelMock()

   let view = TripViewController(viewModelMock)

  

   // when

   view.submitButton.press()

  

   // then

   XCTAssertEqual(viewModelMock.saveTripInvoked)

}

  

class TripViewModelMock: TripViewModel {

   var saveTripInvoked = false

  

   override func saveTrip() {

      self.saveTripInvoked = true

   }

}

 

Swift測試代碼看起來要比Objective C版本更加冗長。事實(shí)上,顯示依賴注入模式會(huì)讓我們更關(guān)注盡可能減少代碼的耦合度。在遷移到Swift之前,我們認(rèn)為我們的Objective -C代碼耦合度很低了。寫了幾周Swift代碼后,''代碼和''代碼之間的差異越來越凸顯。遷移到Swift并正確地測試代碼讓我們的代碼庫耦合度更低。

 

漸入佳境

 

在找到依賴注入的竅門并用這個(gè)方法來寫我們自己的模擬對(duì)象后,我們?cè)?span>Swift中挖掘得更深入了,開始挑選一些別人未嘗涉入的技術(shù)。在前一個(gè)例子我展示了如何重建Objective COCMPartialMock功能。一個(gè)更清晰的方法是用pure mocks而非partial mocks。在Swift中寫靈活的,低耦合代碼的更好方法是使用協(xié)議,以及面向協(xié)議編程技術(shù)。我們很快選擇了這個(gè)方向,代碼變得耦合度更低且更易測試。

 

新語言就有新的語言特性,像guarddefer泛型generics、用do-catch、嵌套類型(nested types)where clause@testable關(guān)鍵字進(jìn)行的錯(cuò)誤處理(error handling),這些僅僅是冰山一角。即便如此Swift依舊很容易上手,有很多內(nèi)容值得深入研究。

 

除了學(xué)習(xí)一門新語言外,我們還從遷移到Swift學(xué)到了什么呢?

 

更易讀的代碼:

 

// Objective C

[self addConstraint:[NSLayoutConstraint constraintWithItem: myButton

        attribute: NSLayoutAttributeCenterX

        relatedBy: NSLayoutRelationEqual

        toItem: myView

        multiplier: 1.0

        constant: 0.0]];

  

// Swift寫的同樣的代碼

self.addConstraint(NSLayoutConstraint(item: myButton,

        attribute: .CenterX,

        relatedBy: .Equal,

        toItem: myView,

        attribute: .CenterX,

        multiplier: 1.0,

        constant: 0.0))

 

Objective-C相比,更苛刻的編譯時(shí)檢查。除了類型安全和編譯時(shí)間因此獲益外,Swift編譯器還對(duì)一些像if表達(dá)式不允許只有一行(not allowing single line if statements )或強(qiáng)制switch表達(dá)式把情況列舉全面(enforcing exhaustive switch statements)等條件做了額外檢查。

 

擺脫頭文件。以及不再需要在.h.m文件之間切換來復(fù)制方法申明。

 

當(dāng)然還有學(xué)習(xí)和使用新語言特性帶來的恐懼

 

相對(duì)優(yōu)勢,劣勢的地方可以說是出人意料的少。一個(gè)明顯的不便是我們的一些第三方依賴--JSONModel--是建立在Objective-C動(dòng)態(tài)特性上的,而這在Swift上并不管用。另一個(gè)問題是我們需要維護(hù)現(xiàn)存的Objective-C代碼,這意味著額外的環(huán)境轉(zhuǎn)換和動(dòng)機(jī)去不斷把更多的Objective-C代碼轉(zhuǎn)換成Swift代碼。

 

當(dāng)然Swift仍是一門新語言,代碼的基礎(chǔ)上,從Objective-C過渡到Swift沒有絲毫降低我們的進(jìn)度。

 

 

 

原文來自:cocoachina

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?