在咱們的工程中處於swift
和OC
混編的狀態,使用swift
已經有一年半的時間了,隨着Xcode9
的更新,swift3.2
和swift4.0
也隨之到來,swift3.2
相較於Xcode8
的swift3.1
變更極小,適配沒遇到問題,主要關注swift4.0
的適配。html
swift
版本 3.2target
target
(pod
引用不用勾選),Nextswift
的 @objc
推斷特性的,若是使用了 swift4.0
顯式的 @objc
屬性,能減小總體代碼的大小。此時咱們選 Minimize Inference(recommend),Minimize Inference(recommend)
根據靜態推斷,僅在須要的地方添加@objc屬性。使用此選項後,須要按照Completing a Swift 4 minimize inference migration來完成轉換。swift
Match Swift 3 Behavior
在編譯器隱式推斷的任何地方向代碼添加一個@objc屬性。這個選項不會改變你的二進制文件的大小,由於被Swift 3隱式推斷在全部的地方都添加了顯式的@objc屬性。xcode
完成上述5步以後,看一下 swift
版本,已是4.0了:
bash
至此打完收工,適配結束。然而並無,當你運行的時候會看到這個:
app
是否欲哭無淚,竟然這麼多錯誤,不用怕,其實要改動的地方並很少,有些都是重複的,能夠直接全局替換就行。ide
舉個栗子:post
// 轉換前
class dynamic func bookMoneyToUpController() -> MPBookMoneyToUpController {
let vc = MPBookMoneyToUpController.init(nibName: "MPBookMoneyToUpController", bundle: Bundle.main)
return vc
}
// 轉換後
class @objc dynamic func bookMoneyToUpController() -> MPBookMoneyToUpController {
let vc = MPBookMoneyToUpController.init(nibName: "MPBookMoneyToUpController", bundle: Bundle.main)
return vc
}
// 問題 @objc 修飾符須要前置
// 修改爲下面便可
@objc class dynamic func bookMoneyToUpController() -> MPBookMoneyToUpController {
let vc = MPBookMoneyToUpController.init(nibName: "MPBookMoneyToUpController", bundle: Bundle.main)
return vc
}
// 全局替換便可
class @objc dynamic func -> @objc class dynamic func複製代碼
注:
上面使用 dynamic
修飾符是因爲之前使用 JSPatch
來作 hotfix
,須要用到原來OC的運行時特性。測試
swift4.0
最大的特性之一就是 @objc
修飾符的變化了,它主要處理 OC
和 swift
混編時一些方法的調用以及屬性獲取問題,swift4.0
將在 swift3.x
中一些隱式類型推斷的特性去除之後,須要咱們來手動管理 @objc
修飾符。
在上文中使用 Xcode
轉換 swift4.0
時咱們勾選了 Minimize Inference
選項,那麼咱們就須要手動處理相關的 @objc
修飾符,來保證 OC
和 swift
代碼能正常相互調用。ui
@objc
修飾符手動處理步驟使用「最小化」轉換代碼後,須要處理構建和運行時的問題,在完成初始的 swift4.0
轉換後,須要按照下面步驟來處理其它問題。this
@objc
的地方測試你的代碼,並修復編譯器提示使用了不推薦的隱式 @objc
引用的警告。直到沒有警告發生。
打開工程的 build settings.
將 Swift 3 @objc inference
設置爲 Default
.
@objc
修飾符須要處理的問題#selector
參數指定的實例方法必須使用 @objc
修飾,由於swift4
中棄用了 @objc
屬性推斷。// 下面的代碼會有警告
class MyClass : NSObject {
func foo() {
}
func bar() {
self.perform(#selector(MyClass.foo)
}
}
warning: argument of ‘#selector’ refers to instance method ‘foo’ in ‘MyClass’ that depends on ‘@objc’ attribute inference deprecated in Swift 4複製代碼
OC
中調用的 swift
方法,在 swift
中須要追加 @objc
修飾,swift4
廢棄了該類型推斷。// 下面的代碼會有警告
@implementation MyClass (ObjCMethods)
- (void)other {
[self foo];
}
@end
warning: Swift method MyClass.foo uses @objc inference deprecated in Swift 4; add @objc to provide an Objective-C entrypoint複製代碼
// 經過追加 @objc 來消除警告
class MyClass : NSObject {
@objc func foo() {
}
func bar() {
self.perform(#selector(MyClass.foo)
}
}複製代碼
@objc
的編譯警告@objc
修飾便可。***Swift runtime:
ClassName.swift:lineInFile:columnInLine:
entrypoint -[ClassName methodName] generated by implicit @objc inference is deprecated and will be removed in Swift 4;
add explicit @objc to the declaration to emit the Objective-C entrypoint in Swift 4 and suppress this message複製代碼
在 Xcode9.1
中,運行時警告在這裏也能看到:
想要修復運行時警告,須要添加 @objc
修飾符到對應的方法或者符號。
運行時警告的常見緣由:
OC
中使用 SEL
swift
中使用了 perform methods
OC
中使用了 performSelector methods
@IBOutlet
或者 @IBAction
// 下面 swift 代碼會產生運行時警告
class MyClass : NSObject {
func foo() {
}
func bar() {
let selectorName = "foo"
self.perform(Selector(selectorName)
}
}
***Swift runtime: MyClass.swift:7:7: entrypoint -[MyClass foo] generated by implicit @objc inference is deprecated and will be removed in Swift 4; add explicit @objc to the declaration to emit the Objective-C entrypoint in Swift 4 and suppress this message複製代碼
swift4.0
其它部分特性NSAttributedString
的初始化方法變化:
// swift3.x
public init(string str: String, attributes attrs: [AnyHashable : Any]? = nil)
// swift4.0
public init(string str: String, attributes attrs: [NSAttributedStringKey : Any]? = nil)複製代碼
示例:
// 轉換前
let attributes = [NSForegroundColorAttributeName: RGB(128, g: 134, b: 146),
NSParagraphStyleAttributeName: paragraph,
NSFontAttributeName: UIFont.systemFont(ofSize: 14)] as [String : Any]
var tipAttrText = NSAttributedString.init(string: tipText, attributes: attributes)
// 轉換後
let attributes = [NSAttributedStringKey.foregroundColor.rawValue: RGB(128, g: 134, b: 146),
NSAttributedStringKey.paragraphStyle: paragraph,
NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)] as! [String : Any]
var tipAttrText = NSAttributedString(string: tipText, attributes: attributes)
// tipAttrText 初始化報錯提示
Cannot convert value of type '[String : Any]' to expected argument type '[NSAttributedStringKey : Any]?'
// 修改
NSAttributedStringKey.foregroundColor.rawValue -> NSAttributedStringKey.foregroundColor
去掉 as! [String : Any]複製代碼
String
的 characters
屬性被廢棄了let string = "abc"
var count = string.characters.count
// 第二行報錯
'characters' is deprecated: Please use String or Substring directly
// 對應新方法
count = string.count複製代碼
String
的 addingPercentEscapes
方法被廢棄了// swift3.x
var url = @"http://www.example.com?username=姓名"
url = url.addingPercentEscapes(using: String.Encoding.utf8)!
// 報錯
'addingPercentEscapes(using:)' is unavailable: Use addingPercentEncoding(withAllowedCharacters:) instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.
// 修改
uri = uri.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!複製代碼
substring(to:)
被廢棄了let index = tagText.index(tagText.startIndex, offsetBy: MPMultipleStyleListItemTagMaxLength)
// 警告:'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range upto' operator.
let b = tagText.substring(to: index)
// 新 API
// 注意:a 的類型是 Substring,不是 String
let a = tagText.prefix(upTo: index)複製代碼
// swift3.x
override class func initialize() {
// some code
}
// 報錯
Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift複製代碼
Swift3.x 繼續 Method Swizzling這篇文章裏面介紹了一種解決思路。
swift3
使用 #selector
指定的方法,只有當方法權限爲 private
時須要加 @objc
修飾符,swift4.0
都要加 @objc
修飾符// 示例代碼
func startMonitor() {
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshUserLoginStatus), name: NSNotification.Name.XSLUserLogin, object: nil)
}
func refreshUserLoginStatus() {
// some code
}
// 第二行警告
Argument of '#selector' refers to instance method 'refreshUserLoginStatus()' in 'MPUnreadMessageCountManager' that depends on '@objc' inference deprecated in Swift 4
// 追加 private
func startMonitor() {
NotificationCenter.default.addObserver(self, selector: #selector(self.refreshUserLoginStatus), name: NSNotification.Name.XSLUserLogin, object: nil)
}
private func refreshUserLoginStatus() {
// some code
}
// 第二行報錯
Argument of '#selector' refers to instance method 'refreshUserLoginStatus()' that is not exposed to Objective-C複製代碼
swift4.0
再也不容許重載 extension
中的方法(包括instance
、static
、class
方法)// 示例代碼
class TestSuperClass: NSObject {
}
extension TestSuperClass {
func test() {
// some code
}
}
class TestClass: TestSuperClass {
// 報錯:Declarations from extensions cannot be overridden yet
override func test() {
// some code
}
}複製代碼
pod
引用添加如下內容到 Podfile
。
post_install do |installer|
installer.pods_project.targets.each do |target|
if ['WTCarouselFlowLayout', 'XSLRevenue', 'OHHTTPStubs/Swift'].include? target.name
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.2'
end
end
end
end複製代碼
UITableViewDelegate
協議方法名變動,沒有錯誤提示:
// swift3.x
func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: IndexPath) -> CGFloat
// swift4.0
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat複製代碼