適配Xcode9.0-beta與Swift4.0

適配Xcode9.0-beta與Swift4.0

原文地址:http://blog.jiar.vip/2017/06/09/%E9%80%82%E9%85%8DXcode9-0-beta%E4%B8%8ESwift4.0/
簡書地址:http://www.jianshu.com/p/1f702d59e54b
您能夠經過保留原文地址或者簡書地址的方式進行轉載。html

這幾天蘋果在開WWDC2017大會,期間放出了Xcode9.0-beta以及Swift4。爲了響應蘋果爸爸的號召,我果斷下載了Xcode9.0-beta,並在項目中拉出了新的分支,準備搞事。ios

Xcode9.0-beta

如何適配

Xcode9.0-beta內置的Swift版本不止一個,它同時支持Swift4.0Swift3.2。而咱們正在用的Xcode8,最高只支持Swift3.1。基於這個事實,我先拉一個Xcode9.0-beta-Swift3.2的分支,待適配好Swift3.2後,再起分支Xcode9.0-beta-Swift4.0去支持Swift4.0git

適配Swift3.2

首先,對於Swift3.2,個人理解是:既然版本命名爲3.2,那麼應該只是基於3.1版本上的微調(我去查Swift,查到更多的是關於Swift4.0方面的信息)。適配Swift3.2的過程當中,個人項目代碼不須要任何改動,惟一出問題的是一個第三方庫:Eureka,報錯的緣由是Collection協議的subscript返回值從Array變成了ArraySlice,關於這個問題,已有人在Eureka的issues中提出(#1082)。隨後有人commit修復了這個問題,並開出新分支來適配Swift3.2github

Eureka-commit

最後,我在Podfile中修改pod 'Eureka'pod 'Eureka', :git => 'https://github.com/xmartlabs/Eureka.git', :branch => 'swift3.2',完成了適配Swift3.2swift

因而可知,適配Swift3.2幾乎是沒有什麼壓力的,我也就看到Collection協議的subscript返回值變更這個狀況。api

適配Swift4.0

並非全部庫都能作到及時支持Swift4.0,更況且是在如今連Xcode9也仍是beta的狀態,因此咱們僅能作到將本身的業務代碼(主工程代碼)部分升級到Swift4.0,而後同時保留各類pod庫在Swift3.2版本。沒辦法,誰叫Swift4.0也還沒法作到ABI兼容呢(希望能在Swift5以前實現吧)。至於我說的同時使用兩個版本的Swift,這是沒問題的,Xcode9支持在項目中同時使用Swift3.2Swift4.0xcode

具體要怎麼作呢?(修改Swift版本)

第一步,以下圖指定主工程的Swift版本爲4.0
Project-Build-Settings-Swift-Language-Version
第二步,以下所示,在Podfile文件的最下方加入以下代碼,指定pod庫的Swift版本爲3.2(這樣會使得全部的第三方pod庫的Swift版本都爲3.2)微信

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.2'
    end 
  end
end

作完以上處理,剩下的就是主工程中的代碼修改了。

Swift3.2Swift4.0的過程,比從Swift3.1Swift3.2的過程要麻煩一點,可是比當年從Swift2.3Swift3的過程要好太多了。app

下面我列舉一下Swift3.2Swift4.0的改變(只是我項目中遇到的):框架

  • Swift4.0中對於擴展的屬性(包括實例屬性、static屬性、class屬性),都只能使用get方法,不可以使用set方法

  • Swift4.0中再也不容許複寫擴展中的方法(包括實例方法、static方法、class方法)

  • swift3使用#selector指定的方法,只有當方法權限爲private時須要加@objc修飾符,如今全都要加@objc修飾符

  • 字體方面的一些重命名(NSFontAttributeName重命名爲NSAttributedStringKey.fontNSForegroundColorAttributeName重命名爲NSAttributedStringKey.foregroundColorNSStrikethroughStyleAttributeName重命名爲NSAttributedStringKey.strikethroughStylesize(withAttributes:)方法重命名爲size(withAttributes:))

  • ...

OCSwift4.0混編纔是坑

因爲歷史緣由,我負責的項目,還有好大一部分OC的代碼,新寫的Swift須要被OC調用。因此,問題來了...

OC調用Swift4.0問題一:編譯不經過

我在Swift4的代碼中寫了很多classextension,有些也給OC調用。在OC的代碼中,咱們經過#import "ModuleName-Swift.h"導入了Swift文件,以給OC調用。若是是Swift3.2,一切都能正常工做,可是在Swift4.0上,編譯通不過了。

一:在OC中調用一個Swift4.0類的方法(包括實例方法、static方法、class方法),你須要:

  • 在該Swift4.0類前加上修飾符@objc

  • Swift4.0類必須繼承NSObject(不然,沒法在前面加上修飾符@objc。固然,這裏指的是普通類,@objc也是能夠修飾UI開頭的一系列UIKit框架下的UI類,只是修飾了這些類,不會產生什麼影響)

  • 在須要調用的方法前加上修飾符@objc
    示例以下:

@objc class SampleObject: NSObject {

    @objc func sampleFunc  {
        print("sampleFunc")
    }
    
    @objc static func sampleStaticFunc  {
        print("sampleStaticFunc")
    }
    
    @objc class func sampleClassFunc  {
        print("sampleClassFunc")
    }

如此一來,即可在OC文件中調用,示例以下:

#import "OCSample.h"
#import "ModuleName-Swift.h"

@implementation OCSample

- (void)callSwiftFunc {
    // 調用實例方法
    SampleObject *object = [[SampleObject alloc] init];
    [object sampleFunc];
    // 調用static方法
    [SampleObject sampleStaticFunc];
    // 調用class方法
    [SampleObject sampleClassFunc];
}

@end

二:在OC中調用一個Swift4.0擴展的屬性(包括實例屬性、static屬性、class屬性)、方法(包括實例方法、static方法、class法),你有以下兩種選擇方式:

  • 在該Swift4.0擴展前加上修飾符@objc(這樣的話,該擴展下的全部的屬性、方法,均可被OC調用)。

示例以下:

@objc extension UIViewController {

    var name: String {
        reutrn "name"
    }
    
    static var staticName: String {
        reutrn "staticName"
    }
    
    class var className: String {
        reutrn "className"
    }
    
    func nameFunc() {
        print("nameFunc")
    }
    
    static func staticNameFunc() {
        print("staticNameFunc")
    }
    
    class func classNameFunc() {
        print("classNameFunc")
    }
    
}
  • 在須要的屬性、方法前直接加上@objc修飾,也可達到目的。

示例以下:

extension UIViewController {

    @objc var name: String {
        reutrn "name"
    }
    
    @objc static var staticName: String {
        reutrn "staticName"
    }
    
    @objc class var className: String {
        reutrn "className"
    }
    
    @objc func nameFunc() {
        print("nameFunc")
    }
    
    @objc static func staticNameFunc() {
        print("staticNameFunc")
    }
    
    @objc class func classNameFunc() {
        print("classNameFunc")
    }
    
}
OC調用Swift4.0問題二:運行時找不到屬性

這個問題藏得比較深,恰巧項目中有着相關的實現,讓我看出發現這個潛在因素。
項目中有這麼一種實現:有一個Swift4.0的類,是繼承UIViewController的。而後我在OC裏面對這個繼承而來的UIViewController進行操做,我用了[viewController valueForKey:@"iconURL"]這一KVC方法去獲取這個自定義UIViewController中的iconURL這一屬性的屬性值。這種方式,編譯時是沒法檢查出問題的。可是在運行時,問題就來了,找不到這個屬性。由於這個屬性沒有暴露給OC來進行調用。

解決方式:僅須要在自定義的UIViewController類中給須要暴露給OC調用的屬性前加上@objc修飾符即可。如此一來,在OC代碼中就能訪問到這個屬性。(注意:這裏可不像上面提到的extension同樣,在這個已定義的UIViewController類前面加上@objc修飾符沒有任何意義)。

示例以下:

class SampleViewController: UIViewController {
    @objc var iconURL: String?
}

除了在OC裏經過valueForKey:方法調用到一些未通過@objc修飾的Swift4.0UI類的屬性會致使crash。其餘好比你在Swift4.0代碼中,經過setValuesForKeys這種經過KVC來操做未通過@objc修飾的屬性,也會致使crash

關於混編方面的更多信息

更多關於混編方面的內容,能夠訪問查看Apple官方提供的這篇文章:Using Swift with Cocoa and Objective-C (Swift 4),篇幅很多,不僅僅介紹了Swift4.0OC的混用,也介紹了與Capi的交互、還有更多關於@objc修飾符的用法。

關於Xcode9-beta的更多

Xcode9-beta局域網調試

要求

  • 必須是Xcode9-beta

  • iPhone系統需iOS11以上

操做

  1. Xcode9-beta菜單的Window選項中選擇Devices and Simulators

  2. 經過鏈接線讓你的Mac識別到你的iPhone

  3. Devices and Simulators面板的左側Connected菜單中選擇鏈接的設備,而後在頂部的DevicesSimulators選項中選擇Devices(這裏其實默認就是選擇了Devices),最後勾選Connect via network選項。

來自stackoverflow回答

結束語

關於本文

  • 本文爲做者這幾天在Xcode9-beta以及Swift4.0方面的學習記錄與分享,做者會視狀況對內容進行補充。

  • 若是您在閱讀本文中發現內容存在錯誤,但願您積極指出。若是您有其餘建議,也歡迎在評論去區留言。

  • 做者接受指正,可是但願彼此之間保留敬意。

  • 歡迎轉載,但請保留博文的原地址或者博文在簡書上的地址。

關於本人

比起 微博@Jiar ,更喜歡 推特@JiarYoo ,求一波關注。?

微信訂閱號

歡迎關注個人我的微信訂閱號,我將不按期分享開發方面的乾貨。

Jiar's 微信訂閱號

相關文章
相關標籤/搜索