轉(zhuǎn)自:
https://github.com/KQAppleDev/SwiftLearn
http://blog.csdn.net/qq_16437739/article/details/52819452
http://www.reibang.com/p/460b5424942a
http://www.qingpingshan.com/rjbc/swift/155193.html
Swift 3 出來也有一陣子了竟闪,Swift 3做了很大的改動,逐漸脫離OC的影子。語法上很多對象去掉了NS開頭刚夺,去掉了繁瑣的命名首有。
本文主要討論Swift 3中的一些坑和使用過程中的一些小技巧卡辰,排名無理由~~
轉(zhuǎn)變?yōu)榻Y(jié)構(gòu)體的類
在之前的Swift版本中耐版,蘋果引入了String、Array和Dictionary這三個結(jié)構(gòu)體來代替OC中的NSString耀石、NSArray和NSDictionary這三個類,當(dāng)然這三個OC類依然可以使用蚪燕。但是在平時的開發(fā)使用中娶牌,Swift的這三個結(jié)構(gòu)體使用起來更方便奔浅,大部分情況下效率更高。 在Swift3.0中诗良,蘋果又推出了以下新的結(jié)構(gòu)體汹桦,原有OC類依然可以使用。并且可以相互轉(zhuǎn)化鉴裹。
常用系統(tǒng)提供單例類的獲取方法Swift風(fēng)格化
Foundation框架部分類名去掉NS前綴
包括:UserDefaults舞骆、URL、NotificationCenter径荔、Bundle督禽、Timer、Thread总处、RunLoop
BOOL屬性的命名規(guī)則
在OC中狈惫,官方建議我們將BOOL屬性的getter方法命名為isXXX,Swift中由于只是將原有OCUIKit框架進行Swift轉(zhuǎn)換,所以這個規(guī)則在之前是Swift中并沒有體現(xiàn)鹦马。
在Swift3.0中胧谈,這個規(guī)則被再次應(yīng)用,所有的BOOL類型都重新命名為isXXX,所以以后我們的自定義類中BOOL屬性的命名也應(yīng)體現(xiàn)這個規(guī)則荸频。
private和fileprivate
- private: 私有屬性和方法菱肖,僅在當(dāng)前類中可以訪問,不包括分類旭从;
- fileprivate: 文件內(nèi)私有屬性和方法稳强,僅在當(dāng)前文件中可以訪問,包括同一個文件中不同的類和悦。
/// 以下所有的類都在同一個文件中
class Test: NSObject {
// 只能在當(dāng)前大括號內(nèi)訪問
private var value: Int = 0
// 只能在當(dāng)前文件內(nèi)訪問
fileprivate var value1: Int = 0
// 只能在當(dāng)前大括號內(nèi)訪問
private func privatePractise() {
value = 1
value1 = 1
fileprivatePractise()
fileprivatePractise1()
print("privatePractise方法被調(diào)用了")
}
// 只能在當(dāng)前文件內(nèi)訪問
fileprivate func fileprivatePractise() {
privatePractise()
fileprivatePractise()
fileprivatePractise1()
print("fileprivatePractise方法被調(diào)用了")
}
}
public和open
在Swift2.3中退疫,pubic有兩層含義:
- 這個元素可以在其他作用域被訪問
- 這個元素可以在其他作用域被繼承或者override
繼承是一件危險的事情。尤其對于一個framework或者module的設(shè)計者而言摹闽。在自身的module內(nèi)蹄咖,類或者屬性對于作者而言是清晰的,能否被繼承或者override都是可控的付鹿。但是對于使用它的人澜汤,作者有時會希望傳達出這個類或者屬性不應(yīng)該被繼承或者修改。這個對應(yīng)的就是 final舵匾。
final的問題在于在標(biāo)記之后俊抵,在任何地方都不能override。而對于lib的設(shè)計者而言坐梯,希望得到的是在module內(nèi)可以被override徽诲,在被import到其他地方后其他用戶使用的時候不能被override。
這就是 open產(chǎn)生的初衷。通過open和public標(biāo)記區(qū)別一個元素在其他module中是只能被訪問還是可以被override谎替。
在Swift3.0中
- public表示當(dāng)前類偷溺、屬性或者方法只能在當(dāng)前module內(nèi)被繼承或者override,在當(dāng)前module意外只能被訪問钱贯;
- open表示當(dāng)前類挫掏、屬性或者方法可以在任何地方被繼承或者override;
- final是一個輔助修飾詞秩命,表示當(dāng)前類尉共、屬性或者方法在任何地方都只能被訪問,不能被繼承或者override弃锐;
internal表示默認級別袄友。
/// ModuleA:
import UIKit
/// 這個類在ModuleA的范圍外是不能被繼承的,只能被訪問
public class NonSubclassableParentClass: NSObject {
// 這個方法在ModuleA的范圍外只能被訪問霹菊,不能被override
public func test() {
print(“test”)
}
//這是**錯誤**的寫法剧蚣,因為class已經(jīng)不能被繼承,
//所以?它的方法的訪問權(quán)限不能大于類的訪問權(quán)限
open func bar() {
print(“bar”)
}
// 這個方法在任何地方都只能被訪問旋廷,不能被override
public final func baz() {
print(“baz”)
}
}
/// 在ModuleA的范圍外可以被繼承
open class SubclassableParentClass: NSObject {
// 這個屬性在ModuleA的范圍外只能被訪問券敌,不能被override
public var size: Int = 0
// 這個方法在ModuleA的范圍外只能被訪問,不能被override
public func foo() {
print(“foo”)
}
// 這個方法在任何地方都可以被override
open func baz() {
print(“baz”)
}
// 這個方法在任何地方都只能被訪問柳洋,不能被override
public final func bar() {
print(“bar”)
}
}
/// 這個類在任何地方都不能被繼承
public final class FinalClass {
}
Swfit3.0中,訪問控制權(quán)限由高到低依次為:open叹坦、public熊镣、internal(默認)、fileprivate募书,private绪囱。
用stride 代替 C-style 循環(huán)
Swift 3 移除了 C 語言風(fēng)格的 for 循環(huán):
for var a = -6.28; a <= 6.28; a += 0.1 {
print(a, separator: " ", terminator: " ")
}
采用stride
for a in stride(from: -6.28, to: 6.28, by: 0.1) { // from ..< to
print(a, separator: " ", terminator: " ")
}
for x in stride(from: -6.28, through: 6.28, by: 0.1) { // from ... to
print(x, separator: " ", terminator: " ")
}
@discardableResult 消除返回值警告
在Swift 3中,如果方法的返回值沒有處理xCode會報一個警告莹捡,如果在方法前加上 @discardableResult
不處理的時候就不會有警告了鬼吵。也可以用
_ = xxx()
來消除警告。
浮點數(shù)取余數(shù)和除法
在Swift 3中 篮赢,如果你聲明一個 let m = 12.0
默認m是 Double, Double是不能和Float做運算的齿椅。 CGFloat在32位設(shè)備上是 Float32 在64位設(shè)備上是 Float64, 所以如果一個 Double 和一個 Float 做運算時先要轉(zhuǎn)換類型的
let m = 12.0
let n:CGFloat = 19.0
let x = CGFloat(m)/n
let k = m.multiplied(by: Double(n)) // 乘法
let y = m.divided(by: Double(n)) // 除法
但是取余算法是不能作用于浮點型的,如果這樣就會報錯 CGFloat(m)%n
正確的做法是:
let z = CGFloat(m).truncatingRemainder(dividingBy: n) //取余 12.0
AnyObject启泣、 Any
這兩個類型都是Swift中很早就出現(xiàn)的類型涣脚,但是我們經(jīng)常使用AnyObject,很少使用Any。
AnyObject類似于OC中的id類型寥茫,表示任意的class的實例對象遣蚀,但是在Swift中,例如我們常見的String和Array都變?yōu)榻Y(jié)構(gòu)體了,
而且在Swift3.0中芭梯,更多的類型或者枚舉被寫為結(jié)構(gòu)體了险耀,AnyObject的適用范圍變相被削弱了,
所以在Swift3.0的API中曾經(jīng)許多AnyOjbect的類型被替換為Any了玖喘。
之前整個項目基本只用 AnyObject 代表大多數(shù)實例,基本也不和 Any 有什么交集甩牺。因為Swift 2 針對 Int、 String 等結(jié)構(gòu)體進行了轉(zhuǎn)換芒涡,編譯器會自動橋接為 NSNumber 和 NSString 這種對象類型柴灯,在swift3中 AnyObject 不能表示結(jié)構(gòu)體了 。而 Any 可以代表 struct费尽、 class赠群、 func 等幾乎所有類型。
這個改動導(dǎo)致項目很多地方都要隨著改旱幼,而且大多數(shù)庫也做了改變查描,如Alamofire的參數(shù)從 [String:AnyObject]? 變成 [String:Any]?
值得一提的是 Any 可以代表任何可空類型,不用指定 Any?
栗子:
let str:String? = "xwwa"
var param:[String:Any] = ["x":1,"code":str]
// ["code": Optional("xwwa"), "x": 1]
str 是一個 Optional 類型的柏卤,輸出出來也是 Optional冬三。因為我們以前的請求是需要在header中帶參數(shù)的json機密,換成 Any 怎么都過不去缘缚,后來發(fā)現(xiàn)有 Optional 值勾笆。
這里寫了個方法轉(zhuǎn)化了下
func absArray(param:[String:Any])->[String:Any]{
let res = param.map { (key,value) -> (String,Any?) in
let newValue = Mirror(reflecting: value)
if newValue.displayStyle == Mirror.DisplayStyle.optional{
if let v = newValue.children.first?.value{
return (key,v)
}else{
return (key,nil)
}
} return (key,value)
}
var newParam:[String:Any] = [:]
res.forEach { (key,v) in
newParam[key] = v
}
return newParam
}
print(absArray(param:param)) // ["code": "xwwa", "x": 1]
用了反射判斷如果值是optional就取出他實際的值.
Swift 3中 Notification 使用方法
Swift 3.0 中NSNotification和Notification創(chuàng)建時,通知的name參數(shù)類型都變?yōu)椤癗otification.Name”類型
extension Notification.Name {
static let kNoticeDemo = Notification.Name("xx.xx.ww.ss")
}
class DE{
func test(){
NotificationCenter.default.post(name:Notification.Name.kNoticeDemo , object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(demo), name: Notification.Name.kNoticeDemo, object: nil)
NotificationCenter.default.removeObserver(self, name: Notification.Name.kNoticeDemo, object: nil)
}
@objc func demo(){
}
}
自定義操作符
在swift 2中自定義操作符
infix operator =~ {
associativity none
precedence 130
}
現(xiàn)在在Swift 3中這樣的話會報警告 Operator should no longer be declared with body;use a precedence group instead
自定義操作符 別名類型
// >>>操作符, 優(yōu)先級別名
infix operator >>> : ATPrecedence
precedencegroup ATPrecedence { //定義運算符優(yōu)先級ATPrecedence
associativity: left
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
}
直接指定操作符的類型桥滨,對這個類型進行定義窝爪,
- associativity: left 表示左結(jié)合
- higherThan 優(yōu)先級高于 AdditionPrecedence 這個是加法的類型
- lowerThan 優(yōu)先級低于 MultiplicationPrecedence 乘除
這里給出常用類型對應(yīng)的group
infix operator || : LogicalDisjunctionPrecedence
infix operator && : LogicalConjunctionPrecedence
infix operator < : ComparisonPrecedence
infix operator <= : ComparisonPrecedence
infix operator > : ComparisonPrecedence
infix operator >= : ComparisonPrecedence
infix operator == : ComparisonPrecedence
infix operator != : ComparisonPrecedence
infix operator === : ComparisonPrecedence
infix operator !== : ComparisonPrecedence
infix operator ~= : ComparisonPrecedence
infix operator ?? : NilCoalescingPrecedence
infix operator + : AdditionPrecedence
infix operator - : AdditionPrecedence
infix operator &+ : AdditionPrecedence
infix operator &- : AdditionPrecedence
infix operator | : AdditionPrecedence
infix operator ^ : AdditionPrecedence
infix operator * : MultiplicationPrecedence
infix operator / : MultiplicationPrecedence
infix operator % : MultiplicationPrecedence
infix operator &* : MultiplicationPrecedence
infix operator & : MultiplicationPrecedence
infix operator << : BitwiseShiftPrecedence
infix operator >> : BitwiseShiftPrecedence
infix operator ..< : RangeFormationPrecedence
infix operator ... : RangeFormationPrecedence
infix operator *= : AssignmentPrecedence
infix operator /= : AssignmentPrecedence
infix operator %= : AssignmentPrecedence
infix operator += : AssignmentPrecedence
infix operator -= : AssignmentPrecedence
infix operator <<= : AssignmentPrecedence
infix operator >>= : AssignmentPrecedence
infix operator &= : AssignmentPrecedence
infix operator ^= : AssignmentPrecedence
infix operator |= : AssignmentPrecedence
合理的使用異常處理,提高代碼質(zhì)量
在日常開發(fā)中齐媒,可能遇到很多特殊情況蒲每,使得程序不能繼續(xù)執(zhí)行下去。有的來自系統(tǒng)語法方面喻括,有的是來自業(yè)務(wù)方面的邀杏。這時候可以使用自定義異常,在底層代碼中不斷throw 在最后一層中去處理唬血。
struct ZError : Error {
let domain: String
let code: Int
}
func canThrow() throws{
let age = 10
if a < 18{
let error = ZError(domain: "xxx", code: 990)
throw error
}
}
do {
try canThrow()
} catch let error as ZError {
print("Error: \(error.code) - \(error.domain)") // Error: 990 - xxx
}
是時候放棄前綴的擴展了
以前我們要給 UIView 擴展是這樣的
extension UIView {
var zz_height:CGFloat{
set(v){
self.frame.size.height = v
}
get{
return self.frame.size.height
}
}
}
這樣在自己寫的屬性前面加一個前綴望蜡。但是Swift 3出來后更多的選擇應(yīng)該是這樣的view.zz.height 。 以前 kingfisher是 imageView.kf_setImage 現(xiàn)在變成 imageView.kf.setImage 刁品。 SnapKit 也改變成了 make.snp.left 之類的語法那么怎么寫這樣的擴展呢泣特?
這里看了 KingFisher 的代碼,給出他的解決方案挑随。比如我們想寫一個UIView的擴展状您。
// 寫一個協(xié)議 定義一個只讀的類型
public protocol UIViewCompatible {
associatedtype CompatableType
var zz: CompatableType { get }
}
public extension UIViewCompatible {
// 指定泛型類型為自身 勒叠, 自身是協(xié)議 誰實現(xiàn)了此協(xié)議就是誰了
public var zz: Auto<Self> {
get { return Auto(self) } // 初始化 傳入自己
set { }
}
}
// Auto是一個接受一個泛型類型的結(jié)構(gòu)體
public struct Auto<Base> {
// 定義該泛型類型屬性
public let base: Base
public init(_ base: Base) {
self.base = base
}
}
// 寫一個Auto的擴展 指定泛型類型是UIView 或者其子類
public extension Auto where Base:UIView {
var height:CGFloat{
set(v){
self.base.frame.size.height = v
} get{
return self.base.frame.size.height
}
}
}
// 擴展 UIView 實現(xiàn) UIViewCompatible 協(xié)議,就擁有了zz屬性 zz又是Auto類型 Auto是用泛型實例化的 這個泛型就是UIView了
extension UIView : UIViewCompatible{
}
// 使用
view.zz.height
上面的注釋已經(jīng)盡量詳細的解釋了這段代碼膏孟,hope you can understand 眯分!
GCD 的改變
swift 3徹底改變了GCD的使用方式,這里給出日常最基本的幾個
你不需要在去用 dispatch_get_main_queue ( ) 來獲取主線程柒桑,而是 DispatchQueue . main 弊决,那么要放到主線程的代碼怎么執(zhí)行呢?只需要在線程后邊使用 . async { } 即可魁淳,也就是說飘诗,大概是這樣:
DispatchQueue.main.async {
print("這里在主線程執(zhí)行")
}
優(yōu)先級
- DISPATCH_QUEUE_PRIORITY_HIGH: .userInitiated
- DISPATCH_QUEUE_PRIORITY_DEFAULT: .default
- DISPATCH_QUEUE_PRIORITY_LOW: .utility
- DISPATCH_QUEUE_PRIORITY_BACKGROUND: .background
//global 中設(shè)置優(yōu)先級 不設(shè)置默認是 default
DispatchQueue.global(qos: .userInitiated).async {
print("設(shè)置優(yōu)先級")
}
創(chuàng)建一個隊列
let queue = DispatchQueue(label: "im.demo.test")
也可以指定優(yōu)先級和隊列
let highQueue = DispatchQueue(
label: "high.demo.test.queue",
qos: DispatchQoS.background,
attributes: DispatchQueue.Attributes.concurrent,
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit,
target: nil
)
highQueue.async {
print("ceshi")
}
3秒后執(zhí)行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0) {
print("after")
}
根據(jù)View查找VC
如果你在一個 UITableViewCell 或者 cell上自定義的一個view上想使用這個view所在的vc怎么辦? 代理 界逛? 層層引用 昆稿? NO ! 一個擴展解決息拜。
一個UIView的擴展
// 查找vc
func responderViewController() -> UIViewController {
var responder: UIResponder! = nil
var next = self.superview
while next != nil {
responder = next?.next
if (responder!.isKind(of: UIViewController.self)){
return (responder as! UIViewController)
}
next = next?.superview
}
return (responder as! UIViewController)
}
記得寫在擴展中哦溉潭,加上前面的技巧 。不論你在哪個view中少欺。只需要這樣let vc = view.zz.responderViewController()
就能拿到所處的vc了喳瓣。
View中的第一響應(yīng)者
又是一個View的擴展也很好用,上代碼
func findFirstResponder()->UIView?{
if self.isFirstResponder{
return self
}
for subView in self.subviews{
let view = subView.findFirstResponder()
if view != nil {
return view
}
}
return nil
}
用法同上赞别,這個東西能干啥呢畏陕?
利用這個可以在 NSNotification.Name.UIKeyboardWillShow 通知通知中拿到第一響應(yīng)者,如果第一響應(yīng)者是UITextfield仿滔,可以算出局底下的距離蹭秋,給擋墻view做變換。避免被覆蓋堤撵。而且這個東西可以寫在協(xié)議的默認實現(xiàn)中或者
寫在一個基類中公用。本文為雜談不細說了羽莺,點到為止咯实昨!
UIViewController 返回是否顯示狀態(tài)欄的方法變化
控制器方法改為屬性
獲取string的字符串長度方法的改變
獲取沙盒指定文件夾路徑的方法變化
獲取文件路徑統(tǒng)一交給FileManager來管理
Swift3.0中selecter的Swift風(fēng)格化
在Swift2.2中,當(dāng)我們?yōu)橐粋€按鈕添加點擊事件時常常這樣寫:
Swift 2.3 中 Selector寫法
在Swift2.2更新到Swift2.3后可以看到警告告訴我們這是一個OC風(fēng)格的寫法盐固,建議改為Swift風(fēng)格的寫法荒给。在Swift3.0中兩種寫法依然都可以使用,但是建議統(tǒng)一寫為Swift風(fēng)格的刁卜,因為你不知道什么時候OC風(fēng)格的就不被允許了志电。
Swift3中字符串處理的變化
//字符串的索引
str = "Hello, Swift"
//str.characters[0] //這是錯誤的
let startIndex = str.startIndex //str字符串的起始Index, 注意它是Index類型的, 并不是Int類型.
str[startIndex] //"H"
//str[startIndex.advancedBy(5)] //"," //swift2
str[str.index(startIndex, offsetBy: 5)] //"," //swift3
//let spaceIndex = startIndex.advancedBy(6) //6 //swift2
let spaceIndex = str.index(startIndex, offsetBy: 6) //6 //swift3
spaceIndex //6
//str[spaceIndex.predecessor()] //"," //spaceIndex前一個字符. //swift2
str[str.index(before: spaceIndex)] //"," //spaceIndex前一個字符. //swift3
//str[spaceIndex.successor()] //"S" spaceIndex后一個字符.
str[str.index(after: spaceIndex)]//"S" spaceIndex后一個字符.
let endIndex = str.endIndex //12 最后一個字符的下標(biāo).
//str[endIndex] //下標(biāo)越界了
//str[endIndex.predecessor()] //用endIndex.predecessor()來表示最后一個字符. //swift2
str[str.index(before: endIndex)] //swift3
str[startIndex..<spaceIndex] //"Hello,"
//let range = startIndex..<spaceIndex.predecessor() //圈定str字符串中的某個區(qū)間. //swift2
let range = startIndex..<str.index(before: spaceIndex) // swift3
//str.replaceRange(range, with: "Hi") //用 "Hi" 替換range中的字符串. //swift2
str.replaceSubrange(range, with: "Hi") //swift3
//str.appendContentsOf("123") // "Hi, Swift123" /swift2
str.append("123") //swift3
//str.insert("?", atIndex: str.endIndex) //"Hi, Swift123" //swift2
str.insert("?", at: str.endIndex) //swfit3
//str.removeAtIndex(str.endIndex.predecessor()) //"?" //swift2
str.remove(at: str.index(before: str.endIndex)) //swift3
str //"Hi, Swift123"
//str.removeRange(str.endIndex.advancedBy(-2)..<str.endIndex) //"Hi, Swift1" 刪除后兩個字符. //swift2
str.removeSubrange(str.index(str.endIndex, offsetBy: -2)..<str.endIndex) //swift3
//str.uppercaseString //"HI, SWIFT1" //swift2
str.uppercased() //swift3
//str.lowercaseString //"hi, swift1" //swift2
str.lowercased() //swift3
//str.capitalizedString //"Hi, Swift1" 單詞首字母大寫 方法 //swift2
str.capitalized //swift3
//str.containsString("Hello") //false //swift2
str.contains("Hello") //swift3
str.hasPrefix("Hi") //true 是否以"Hi"開頭
str.hasSuffix("ft1") //true 是否以"ft1"結(jié)束
let p1 = "one third is \(1.0/3.0)" //"one third is 0.333333333333333"
// 使用OC中的NSString 格式化保留兩位小數(shù), 并使用as強制轉(zhuǎn)換為Swift的String類型.
let p2:String = NSString(format: "one third is %.2f", 1.0/3.0) as String
var p3:NSString = "one third is 0.33" //聲明并賦值一個NSString字符串變量.
//p3.substringFromIndex(4) //NSString截取字符從第4個字符開始. //swift2
p3.substring(from: 4) //swift3
//p3.substringToIndex(3) //NSString截取字符到第3個字符. //swift2
p3.substring(to: 3) //swift3
//p3.substringWithRange(NSMakeRange(4, 5)) //"third" 4~9 //swift2
p3.substring(with: NSRange(location: 4, length: 5)) //swift3