前言
swift/37122.html">swift4.0已經出來一段時間,之前已經給大家總結介紹了關于swift4的新特性,那么本文就來介紹下當swift升級到swift4在使用中會遇到哪些問題呢?下面話不多說了,來一起看看詳細的介紹吧。
升級Swift4.0
一. 修改Swift版本
1. 如下圖指定主工程的Swift版本為4.0
2. 修改pod庫
在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 endend
二. 主工程中的代碼修改
1. 列舉一下Swift3.2到Swift4.0的改變(只是我項目中遇到的):
1). Swift4.0中對于擴展的屬性(包括實例屬性、static屬性、class屬性),都只能使用get方法,不可使用set方法
2). Swift4.0中不再允許復寫擴展中的方法(包括實例方法、static方法、class方法)
比如:自定義的協議方法在extension中實現,若某個類遵循了該協議,其子類便不能重寫該協議方法
解決的方法是: 在每個需要該協議的類里面都重新遵循該協議,實現協議方法
個人想到的辦法,不知道有沒有其他解決辦法可以提供一下
3). swift3使用#selector指定的方法,只有當方法權限為private時需要加@objc修飾符,現在Swift4.0全都要加@objc修飾符
4). 自定義的protocol協議中,有optional修飾的非必須實現的方法,需要用@objc修飾
5). 字體方面的一些重命名
NSFontAttributeName --- .font //或者NSAttributedStringKey.fontNSForegroundColorAttributeName --- .foregroundColor//NSAttributedStringKey.foregroundColorNSStrikethroughStyleAttributeName --- .strikethroughStyle//NSAttributedStringKey.strikethroughStyle//字符串類型的,添加rawValueNSAttributedStringKey.font.rawValue//等等等等..........//大部分類似以下,涉及富文本的方法均已改為了NSAttributedStringKey類型addAttributes(_ attrs: [NSAttributedStringKey : Any] = [:], range: NSRange)
三. 項目中遇到的一些的報錯問題
3-1. "Closure cannot implicitly capture a mutating self parameter"錯誤
在struct中,如果我們在閉包中使用self,就會得到Closure cannot implicitly capture a mutating self parameter的錯誤提示。比如:
struct RecordModel { /// 定義一個閉包 var action: (() -> ())? var height = 10 self.action = { self.height = 20 //Closure cannot implicitly capture a mutating self parameter報錯 }}
++并且由于RecordModel的類型是struct,我們也沒發在action閉包里添加截獲列表。那么是不是就必須使用class了?答案是否定的。有兩種方式可以解決這個問題。++
方案一:為closure增加一個inout類型的參數
struct RecordModel { /// 定義一個閉包 var action: ((_ inSelf: inout RecordModel) -> ())? var height = 10 self.action = { (inSelf) in inSelf.height = 20 }}
根據inout類型的說明,我們知道,實際上這相當于增加了一個隱藏的臨時變量,self被復制,然后在closure(閉包)中使用,完成后,再復制回self。也就是說,這個方法有額外的內存開銷。如果是struct較大的情形,這么做并不劃算。
方案二:使用UnsafeMutablePointer<Pointee>
==這次采用直接指針的方式對于struct來進行操作,采用指針的好處是self不會被多次復制,性能較高。缺點是你需要自行確定你的代碼的安全。==
struct RecordModel { /// 定義一個閉包 var action: (() -> ())? var height = 10 let selfPointer = UnsafeMutablePointer(&self) self.action = { selfPointer.pointee.height = 20 }}
結論
==Closure cannot implicitly capture a mutating self parameter錯誤的原因是在進出closure(閉包)之后,self的一致性沒辦法得到保證,所以編譯器默認不允許在struct的closure(閉包)中使用self。如果我們確定這么做是安全的,就可以通過上面的兩種方式解決這個問題。其中,方法二的性能更好一些。==
注意
這里可以記一下指針和swift變量之間的關系:
3-2. Declarations from extensions cannot be overridden yet 錯誤
==這個錯誤大致是因為,協議方法是在extension里面的,不能被重寫==
解決辦法:(僅供參考,如有更好的建議還望多多指教)
小編想到的解決辦法就是在每一個需要此協議的類里面,重新遵循代理,實現該協議方法
3-3. "Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift"
==報錯原因: 在于已經廢棄的initialize方法,示例如下==
方法交叉(Method Swizzling)
有時為了方便,也有可能是解決某些框架內的 bug,或者別無他法時,需要修改一個已經存在類的方法的行為。方法交叉可以讓你交換兩個方法的實現,相當于是用你寫的方法來重載原有方法,并且還能夠是原有方法的行為保持不變。
extension UIViewController { public override class func initialize() {//此處報錯 //此處省略100行代碼 }}
initialize該方法已經被Swift4.0廢棄
在Swift3.0還勉強可以使用,但是會有警告;但是在4.0已經被完全廢棄
==替代方法:==
在 app delegate 中實現方法交叉
像上面通過類擴展進行方法交叉,而是簡單地在 app delegate 的 application(_:didFinishLaunchingWithOptions:) 方法調用時調用該方法
extension UIViewController { public override class func initializeOnceMethod() { //此處省略100行代碼 }}//在AppDelegate的方法中調用:func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { //此處省略100行代碼 UIViewController.initializeOnceMethod()}
3-4. 'dispatch_once' is unavailable in Swift: Use lazily initialized globals instead
報錯原因: dispatch_once在Swift4.0也已經被廢棄
extension UITableView { struct once{ static var onceTaken:Int = 0 } dispatch_once(&once.onceTaken) { () -> Void in //在這里dispatch_once就會報錯 //此處省略1000000行代碼 }}
解決方法: 通過給DispatchQueue添加擴展實現
extension DispatchQueue { private static var _onceTracker = [String]() public class func once(token: String, block: () -> ()) { objc_sync_enter(self) defer { objc_sync_exit(self) } if _onceTracker.contains(token) { return } _onceTracker.append(token) block() } func async(block: @escaping ()->()) { self.async(execute: block) } func after(time: DispatchTime, block: @escaping ()->()) { self.asyncAfter(deadline: time, execute: block) }}
使用字符串token作為once的ID,執行once的時候加了一個鎖,避免多線程下的token判斷不準確的問題。
使用的時候可以傳token
DispatchQueue.once(token: "tableViewOnce") { print( "Do This Once!" ) }
或者使用UUID也可以:
private let _onceToken = NSUUID().uuidString DispatchQueue.once(token: _onceToken) { print( "Do This Once!" ) }
四、swift3.2升級到swift4.0 掃碼不走回調方法
xcode升級到9.0 swift改到swift4.0之后掃碼一直不走回調 ,研究了好長時間,發現蘋果把掃碼的代理方法的參數變了之前的方法
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!)
這是之前swift3.2的代理方法,swift4.0之后不會走這兩個代理方法,原因是現在代理方法不一樣了
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)
swift4.0的兩個代理方法對比之前的3.2方法,可以發現現在方法的參數變了
將之前的兩個方法的參數改好,掃碼就可以正常用了
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。
新聞熱點
疑難解答