中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

遷移Swift3.0爬坑與Swift交互OC之變化

2018-07-20    來(lái)源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用

都知道蘋果要在下個(gè)版本的Xcode中移除Swift2.3的支持,強(qiáng)制開(kāi)發(fā)者使用Swift3.0,這是一個(gè)很悲痛的現(xiàn)實(shí)。然而正好公司的項(xiàng)目是OC和Swift混編的項(xiàng)目,里面用到了一個(gè)第三方庫(kù) SwiftBond ,當(dāng)時(shí)SwiftBond還沒(méi)有升級(jí)Swift3.0,老大害怕是個(gè)坑,所以就讓我使用RxSwift去替換掉這個(gè)庫(kù),然而正當(dāng)我要?jiǎng)邮值臅r(shí)候,突然發(fā)現(xiàn)我要把項(xiàng)目升級(jí)到Swift3.0啊,不然換了RxSwift沒(méi)有卵用。!??

讓我45度角仰望星空,我的悲傷逆流成河!

沒(méi)辦法誰(shuí)讓蘋果逼的緊呢,正好也能提升一下自己Swift的水平,所以就開(kāi)干了,沒(méi)想到在這個(gè)過(guò)程中我的悲傷卻逆流成海了?。

我發(fā)現(xiàn)原來(lái)項(xiàng)目中使用的Swift寫的代碼簡(jiǎn)直不能瞅,像我這種對(duì)代碼潔癖的很多地方都進(jìn)行了重寫。并且原來(lái)的Swift代碼也并沒(méi)有按照Swift文件中的標(biāo)準(zhǔn)來(lái)寫,導(dǎo)致里面坑巨多,使用Convert轉(zhuǎn)換以后,每個(gè)Swift文件中幾乎都是一二百個(gè)錯(cuò)誤,我只能一個(gè)一個(gè)手動(dòng)去改,而且還遇到了非常難以發(fā)現(xiàn)的巨坑,不過(guò)到頭來(lái)我還是成功地把項(xiàng)目遷移到了Swift3.0,并且把SwiftBond替換為了RxSwift?。由于這個(gè)過(guò)程中坑非常多,特此總結(jié)下來(lái),以免大伙遇到此坑以后無(wú)從下手。

去除@objc

項(xiàng)目中很多地方都使用了 @objc 和 dynamic 關(guān)鍵字修飾,例如:

@objc var clockInShare: Int = 0
@objc dynamic funcsetupModels() { ... }

將所有的繼承了NSObject的里面的非private方法和屬性前的 @objc 和 dynamic 關(guān)鍵字去掉,因?yàn)槔^承了NSObject的類,Swift會(huì)默認(rèn)在前面添加@objc關(guān)鍵字,而dynamic關(guān)鍵字一般使用KVO等動(dòng)態(tài)特性的時(shí)候才用的到。

使用extensnion進(jìn)行歸類

有些文件中的類在一個(gè)大括號(hào){}中包含了全部的屬性和方法,或者還是和OC寫法一樣,一下繼承了 UITableViewDataSource , UITableViewDelegate ,在里面使用了//MARK: 進(jìn)行分類,但我感覺(jué)這種寫法太亂了。所以將他們?nèi)渴褂胑xtension進(jìn)行分類,這樣子更符合Swift語(yǔ)言的優(yōu)美風(fēng)格

// MARK: - Actions 
@objc dynamic funcbi_UnselectedWord() {
}

更改為:

extensionCCSequenceExerciseViewController{
    funcbi_UnselectedWord() {}
}
extensionCCSequenceExerciseViewController:UITableViewDataSource,UITableViewDelegate{ ... }

更改為:

extensionCCSequenceExerciseViewController:UITableViewDataSource{ ... }
extensionCCSequenceExerciseViewController:UITableViewDelegate{ ... }

使用extension分類的時(shí)候也有一個(gè)改變,原來(lái)類中使用的private關(guān)鍵字,Swift2中extension中是可以訪問(wèn)這個(gè)private屬性,但是到了Swift3.0中private屬性作用域變?yōu)榱藍(lán)}之間,所以extension就不能訪問(wèn)了。蘋果又新添加了一個(gè)關(guān)鍵字為fileprivate表示只能在這個(gè)文件中被訪問(wèn),換成這個(gè)關(guān)鍵字就可以了

閉包更改

原來(lái)Swift2.3中閉包的聲明是這樣子寫的:

typealias Command = ()->()
var buttonCommand = Command?()

Swift3.0編譯會(huì)提示修改,更改為如下:

typealias Command = ()->()
var buttonCommand = Command?()

去除Swift文件中的NS前綴的類

Swift3.0中把大量的帶有NS的類型去掉了NS前綴,與OC交互的時(shí)候,Swift調(diào)用OC方法中的返回值會(huì)默認(rèn)為Swift中類型,也就是說(shuō)默認(rèn)把類類型轉(zhuǎn)換為了Swift中的值類型,比如OC方法返回NSArray那么Swift中會(huì)默認(rèn)為Array,我簡(jiǎn)單測(cè)試了幾個(gè)常用的返回類型,如下:

OC Swift
NSArray Array
NSDictionary Dictionary
NSString String
id Any
NSDate Date
NSNumber NSNumer
NSInteger Int

可以看到原來(lái)OC中的id對(duì)應(yīng)Swift中的AnyObject,現(xiàn)在更改為對(duì)應(yīng)Swift中的Any類型,靈活性更高了,這個(gè)要注意。

OC中的NSNumbe仍然對(duì)應(yīng)Swift中的NSNumber(使用NSNumber會(huì)有一個(gè)大坑,后面會(huì)說(shuō)到)。

發(fā)現(xiàn)我們項(xiàng)目中的Swift文件中使用了很多的NSArray,NSDictionary,NSString,NSDate,這可能是歷史的原因吧。因?yàn)镾wift建議盡量使用Swift中內(nèi)置的一些類型,并且Swift3.0已經(jīng)默認(rèn)轉(zhuǎn)為不帶NS前綴的類型了,雖然項(xiàng)目使用NS前綴的也能運(yùn)行,但是我對(duì)代碼有潔癖,把所有使用到NS的地方全部重寫了,換成了不帶NS前綴的Swift類型。

比如:

let cloudTime = NSDate().dateByAddingTimeInterval(NSUserDefaults.standardUserDefaults().cc_TimeDiffToServer)

更改為

let cloudTime = Date().addingTimeInterval(UserDefaults.standard.cc_TimeDiffToServer)

再比如:

@objc dynamic funcgetSavedCheckInInfo() -> NSDictionary{
 	.....
    return CCDataDownHelper.fetchDataWithKey(key) as! NSDictionary
}

更改為

funcgetSavedCheckInInfo() -> Dictionary<String, AnyObject>? {
	.....
    return (checkInInfo as? Dictionary<String, AnyObject>)
 }

不帶NS前綴的類型沒(méi)有某個(gè)方法

注意有時(shí)候Swift內(nèi)置類型并沒(méi)有包含帶有NS前綴類型里面的所有方法,如果如果我們使用Swift類型調(diào)用這些方法,會(huì)提示沒(méi)有這個(gè)方法,細(xì)心的你會(huì)發(fā)現(xiàn)這個(gè)方法是帶有NS前綴的類型才有的方法,所以我們必須將Swift類型轉(zhuǎn)換為NS前綴的類型才能調(diào)用此方法。

例如:

let userDic = ["ttf": "123"]
userDic.write(toFile: filePath, atomically: true)

這時(shí)候會(huì)報(bào)一個(gè)錯(cuò)誤: value of type [String: String] has no member write ,意思就是沒(méi)有這個(gè)方法,這時(shí)候我們就需要將他轉(zhuǎn)為帶有NS前綴的類型了

let userDic = ["ttf": "123"]
(userDic as NSDictionary).write(toFile: filePath, atomically: true)

但還是要注意在Swift文件中盡最大可能滴使用Swift的數(shù)據(jù)類型。

可選值的使用

因?yàn)镾wift的出現(xiàn),OC中也添加了幾個(gè)關(guān)鍵字 nullable , nonnull 等關(guān)鍵字來(lái)修飾參數(shù)和返回值。OC文件中的返回值如果不包含這幾個(gè)關(guān)鍵字,Swift調(diào)用OC的方法默認(rèn)的返回值類型是一個(gè)optional類型,如果你添加了nonnull關(guān)鍵字來(lái)修飾,Swift中得到的值就是一個(gè)非optional的普通值。

然而我們項(xiàng)目中原來(lái)的OC方法的返回值都是不包含任何關(guān)鍵字的,所以Swift去使用OC的時(shí)候就很蛋疼了,每個(gè)返回值都要去處理一下。而且我看到原來(lái)文件里面有這樣去處理這個(gè)值的:

let bgcfg = CCBgcfgService()
let copywriterMode = bgcfg.inquireDataWithType(.Copywriter, subType:.CopywriteCheckInShare)
var array = NSArray()
if copywriterMode != nil {
    array = copywriterMode.valueForKey("texts") as! NSArray
}

看到這里我又默默地重寫了整個(gè)Swift的文件,這里copywriterMode是OC方法返回的一個(gè)可選值,不應(yīng)該使用OC里面的處理方式這個(gè)optional值。更改為:

let copyWriterMode = bgcfg.inquireData(with: .Copywriter, subType:.CopywriteCheckInShare)
    
var array: Array<AnyObject>? = nil
if let writeMode = copyWriterMode as? CCBackgroundCfgCopywriterModel {
    array = writeMode.value(forKey: "texts") as? Array<AnyObject>
}

最好使用可選綁定,或者使用 guard let 來(lái)處理optional的值,項(xiàng)目中有很多這樣的地方全部讓我重寫了?,想想都累。

下面這個(gè)是處理服務(wù)器端返回的值

let obj:AnyObject = response.originalContent
if !(obj is NSDictionary) {
    failure(reason: "")
    return;
}
success(dic: (obj as! NSDictionary))

更改為:

guard let response = response else { return }
let obj = response.originalContent as? Dictionary<String, AnyObject>
if let obj = obj {
    success(obj)
} else {
    failure("")
}

注意:如果你寫OC方法一定要加上 nullable , nonnull 等關(guān)鍵字修飾,Swift中處理optional值的時(shí)候盡量去選擇使用可選綁定或者guard let

巨坑一 NSNumber

其實(shí)更改Swift3.0,我搞了兩遍,第一遍手動(dòng)把編譯錯(cuò)誤全部消除掉以后,發(fā)現(xiàn)木有錯(cuò)誤了,我小心翼翼地按下了common+B,編譯的正爽的時(shí)候,突然一個(gè)紅色感嘆號(hào)出來(lái)了,一個(gè)錯(cuò)誤編譯錯(cuò)誤

Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code

但是每個(gè)頁(yè)面都確實(shí)沒(méi)有錯(cuò)誤?而且沒(méi)有任何錯(cuò)誤提示,也實(shí)在找不到任何有用的錯(cuò)誤信息。

后來(lái)搞了好久,實(shí)在沒(méi)有辦法,就搞了一個(gè)笨辦法,重搞項(xiàng)目,把所有的Swift寫的模塊全部移除,一個(gè)模塊一個(gè)模塊的添加,一個(gè)模塊一個(gè)模塊的遷移Swift3.0,保證每個(gè)模塊編譯通過(guò)以后添加下一個(gè)模塊,后來(lái)添加了一個(gè)swift文件,編譯又報(bào)了這個(gè)錯(cuò)誤,我就在這個(gè)文件中一行一行的注釋,最終發(fā)現(xiàn)了問(wèn)題的所在:

let attributeTitle = NSAttributedString(string: "PK", attributes: [NSBaselineOffsetAttributeName : NSNumber(int: -1)])

就是因?yàn)檫@個(gè)NSNumber的使用導(dǎo)致這個(gè)Swift編譯器的錯(cuò)誤,而且頁(yè)面也不報(bào)錯(cuò),不知道是不是Swift編譯器的bug還是其他原因,有知道的小伙伴可以留言告訴我一下。

更改為:

let attributeTitle = NSAttributedString(string: "PK", attributes: [NSBaselineOffsetAttributeName : -1.0])

說(shuō)實(shí)話這個(gè)坑實(shí)在是太難找,后來(lái)又添加一個(gè)Swift文件,又出現(xiàn)這個(gè)問(wèn)題,我就直接搜NSNumber,果然是有,把NSNmber去掉以后,編譯通過(guò)。如果有小伙伴也遇到這個(gè)錯(cuò)誤,可以嘗試搜下NSNumber更換,錯(cuò)誤應(yīng)該會(huì)解決。

巨坑二 重寫OC方法

我們項(xiàng)目中有幾個(gè)使用Swift寫的Interceptor,他繼承一個(gè)OC的協(xié)議,并且重寫了OC的方法,每打開(kāi)一個(gè)頁(yè)面都是去執(zhí)行每個(gè)攔截器文件中的方法,但是我把項(xiàng)目升級(jí)到Swift3.0以后,這幾個(gè)Swift寫的攔截器就再也沒(méi)有執(zhí)行過(guò),對(duì)比了好多遍重寫的方法確實(shí)和OC定義的一模一樣?頁(yè)面上也沒(méi)有任何報(bào)錯(cuò),項(xiàng)目也可以編譯成功?

后來(lái)實(shí)在搞不懂了就去請(qǐng)教了公司的一位大神,才明白因?yàn)镾wift3.0的API大變,Swift去重寫OC方法的時(shí)候,其實(shí)并不是要去重寫OC聲明的方法,而是要去重寫OC轉(zhuǎn)換為Swift所聲明的方法。例如一個(gè)OC協(xié)議是這樣

@protocolNavigatorInterceptor<NSObject>
@optional
- (void)interceptOpenWithContext:(HJNavigatorInterceptorContext *)context;
@end

Swift文件繼承這個(gè)協(xié)議不能去直接實(shí)現(xiàn)這個(gè)方法

extensionStrangeWordBookNavigatorInterceptor:NavigatorInterceptor{
	funcinterceptOpenWithContext(context: HJNavigatorInterceptorContext!) { }
}

在Swift2.3中這樣實(shí)現(xiàn)是可以的,但是到了Swift3中,這樣子實(shí)現(xiàn)就錯(cuò)誤了。永遠(yuǎn)都不會(huì)執(zhí)行這段代碼。重寫OC方法的時(shí)候首先要看OC方法生成的Swift方法是什么樣

可以看到轉(zhuǎn)換成Swift對(duì)應(yīng)的文件中聲明的方法是和原來(lái)的不一樣的,我們應(yīng)該實(shí)現(xiàn)Swift中對(duì)應(yīng)的方法。

extensionCCStrangeWordBookNavigatorInterceptor:HJNavigatorInterceptor{
    funcinterceptOpen(with context: HJNavigatorInterceptorContext!) {}
}

這樣子程序就正常運(yùn)行了,每一個(gè)使用Swift所寫的攔截器都會(huì)走了。

另外提醒大伙一句:從這個(gè)坑可以知道,以后我們使用Swift調(diào)用OC的方法的時(shí)候都要先去看看OC生成對(duì)應(yīng)Swift版本的方法是什么樣子,這樣子才能保證程序的穩(wěn)定,雖然我測(cè)試的Swift直接調(diào)用OC類型的方法暫時(shí)不會(huì)有啥問(wèn)題,但最好還是改為Swift的。我就花了很多時(shí)候?qū)㈨?xiàng)目中Swift調(diào)用OC的方法全部改為對(duì)應(yīng)Swift的版本了。

巨坑三 介詞

Swift3.0將方法中的介詞都轉(zhuǎn)移到了括號(hào)里面。比如:

  • UIFont.systemFontOfSize(14) 改為 UIFont.systemFont(ofSize: 14)
  • writeToFile() 改為 write(toFile:)
  • initWithName(name: String) 改為 init(with name: String)
  • NSJSONSerialization.dataWithJSONObject(JSONArray, options:) 改為 JSONSerialization.data(withJSONObject: JSONArray as Any, options:)

反正只要有介詞的方法都做了改變,包括OC方法的Swift版本,完全不一樣了,這就是為什么調(diào)用或者重寫OC方法的時(shí)候一定要去看一下他所對(duì)應(yīng)的Swift版本。

最坑的就是如果你Swift中有些地方還是原來(lái)的介詞在外面的寫法,但是Xcode并不給錯(cuò)誤提示,編譯也可以通過(guò),但是你運(yùn)行程序走到那個(gè)地方程序直接就crash了,真是無(wú)語(yǔ),例如下面這個(gè)地方就一直crash但沒(méi)有錯(cuò)誤提示

let s = subjects.removeAtIndex(index)
    
if s.subjectType.rawValue == 9 {
    s.options = s.options.lowercaseString
    s.answerOption = s.answerOption.lowercaseString
}
    
self.subjects.append(s)
s.index = self.subjects.indexOf(s)!

更改為:

let s = subjects.remove(at: index)
    
if s.subjectType.rawValue == 9 {
    s.options = s.options.lowercased()
    s.answerOption = s.answerOption.lowercased()
}
    
self.subjects.append(s)
s.index = self.subjects.index(of: s)!

剩下的大部分更改也只是語(yǔ)法問(wèn)題,如果你的Swift項(xiàng)目是按照Swift語(yǔ)言標(biāo)準(zhǔn)來(lái)寫的,那么你Convert到Swift3.0非常輕松,幾乎沒(méi)有什么錯(cuò)誤,有的話也只是一點(diǎn)小小的語(yǔ)法問(wèn)題,就像我們項(xiàng)目中的watch版本完全純Swift寫的,一鍵convert swift3.0 一點(diǎn)錯(cuò)誤都沒(méi)有,直接運(yùn)行。

 

來(lái)自:http://codertian.com/2016/12/17/Swift3-0-pa-keng-ji-jin/

 

標(biāo)簽: 代碼 服務(wù)器 服務(wù)器端 開(kāi)發(fā)者

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:Android路由實(shí)現(xiàn)

下一篇:Android手把手教你實(shí)現(xiàn)搜索框