主要總結一些平時遇到的疑難點,在此總結出來惶我,持續(xù)更新〔┩叮可能有些誤導大家的地方绸贡,歡迎指正。
難點
-
get,set,willSet,didSet
??get
計算屬性毅哗,主要用來計算返回值
基本語法:struct Point { var y: Int? var x: Int? var loation:(Int?, Int?) { return (x, y) } }
實現<code>read-only</code>:
Swift中的屬性沒有對應的實例變量恃轩,必須自己創(chuàng)建。
struct Point {
private var _y: Int?
var y:Int { return _y }
}
??set
存儲屬性黎做,主要存儲值叉跛。
設置存儲屬性時,必須實現計算屬性蒸殿。
struct Point {
var y: Int?
var x: Int?
var loation:(Int?, Int?) {
get { return (x, y) }
set {
x = newValue.0
y = newValue.1
}
}
}
??willSet筷厘、didSet是觀察器
<code>willSet</code>在新值設置之前調用,傳入默認參數<code>newValue</code>宏所。
<code>didSet</code>在新值設置之后調用酥艳,傳入默認參數<code>oldValue</code>。
觀察器觀察的不止是對象本身爬骤,對象的某個屬性的值改變也會觸發(fā)觀察器充石。
??設置觀察期后就不能實現計算屬性和存儲屬性。
- AnyObject, Any, 泛型
?? AnyObject
定義是一個所有類都遵守的協議霞玄,并且適用于OC骤铃。相當于OC的<code>id</code>類型,應該是為了兼容Cocoa框架坷剧。
在OC和Swift混編時惰爬,很多類型不兼容,就可以<code>AnyObject</code>做過渡惫企。AnyObject也主要是為了兼容OC撕瞧,Swift編程時使用<code>AnyObject</code>會將類型轉換為OC對應的類型,例如123會轉為<code>NSNumber而不</code>是<code>Int</code>。
例如對于Swift中用到的<code>NSArray</code>中的成員丛版,當不知道<code>NSArray</code>中對象的類型時就使用<code>AnyObject</code>巩掺。
?? Any
Any是Swift中定義的類似<code>AnyObject</code>的協議,定義所有類型(包括函數页畦、枚舉胖替、結構體等)都遵守的協議。<code>Any</code>的范圍比<code>AnyObject</code>更廣寇漫。
適用于不知道類型的場景,也使程序更加靈活殉摔。
?? 泛型
泛型和<code>Any</code>基本一致州胳,都是為了增加程序的靈活性,但是泛型多了類型檢測逸月,可以保證特定類型栓撞。
例如:
func exchange<T>(x: T, y: T) { // exchange } - 協議,optional
Swift協議不能實現可選碗硬。只能通過<code>@objc</code>實現可選瓤湘。
但是Swift協議可擴展也可以繼承。
Swift的協議實現一般通過擴展實現恩尾,通過協議繼承和類的擴展來實現<code>optional</code>弛说。
協議的命名遵循 Swift 的標準庫, 即協議名以 "Type", "-able"翰意, "-ible" 結尾木人。-type 定義行為, -able、-ible 定義元素怎樣做某事冀偶。
protocol AppleType {
func eatApple()
}
class Apple {}
extension Apple: AppleType {
func eatApple() {}
}
函數
-
默認參數
方法可以通過設置默認參數醒第,實現多參數時參數為空的情況,也可以簡寫方法进鸠,讓方法更靈活稠曼。func abc(a: Int, b: Int? = nil, c: Int?) -> Int { return a + (b ?? 0) + (c ?? 0) } abc(a: 1, c: 2) abc(a: 1, b: 1, c: nil)
-
傳出參數
使用<code>inout</code>實現傳出參數,但是作為傳出參數時客年,不能使用Optional值潭陪。func exchange<T>(a: inout T, b: inout T){ let c = a a = b b = c } var aa = 10 var bb = 20 exchange(a: &aa, b: &bb) aa // 20 bb // 10
-
省略參數
func sum(data: Int...) -> Int{ var result = 0 for item in data { result += item } return result } sum(data: 1, 2, 3, 4)
-
泛型方法
// 自定義帶索引的for_in func forin<T>(_ items: [T], closure: (T, Int) -> Void) { var index = 0 for item in items { closure(item, index) index += 1 } } forin([1,3,4]) { item, index in print("\(item)....\(index)") }
Curring
柯里化:《Advanced Swift》中提出的一種通過寫方法模板來快速實現方法的機制攀操。讓我們的方法更加動態(tài)化,可以整合系統(tǒng)方法。
Curring將接受多個參數的函數段磨,轉化成每次接受一個參數的調用序列。
Curring主要是一種新的表現形式诱鞠,主要看個人喜好闹瞧,并沒有什么很特別的地方。
下面介紹兩種Curring的方法:
-
自定義的簡單Curring
// 普通閉包實現map let numbers = 1...10 let result = numbers.map { a in return a + 1 } // Curring func add(_ a: Int) -> (Int) -> Int { return { b in return a + b } } result = numbers.map(add(1))
-
封裝的第三方Curring
Github上已經有一套封裝好的Curry方法,可以通過使用這些方法可以快速實現美麗的多參數調用铅鲤。//curry方法實現 func curry<A, B>(_ function: @escaping (A) -> B) -> (A) -> B { return { a in function(a) } } func curry<A, B, C>(_ function: @escaping (A, B) -> C) -> (A) -> (B) -> C { return { a in { b in return function(a, b) } } } func curry<A, B, C, D>(_ function: @escaping (A, B, C) -> D) -> (A) -> (B) -> (C) -> D { return { a in { b in { c in return function(a, b, c) }}} } // curry調用 func addThree(a: Int, b: Int, c: Int) -> Int { return a + b + c } let result = curry(addThree)(1)(2)(3)
關鍵字
-
typealias
這是一個非常好用的關鍵字划提,用來定義別名。系統(tǒng)使用了大量別名邢享。
可以定義一些有意義的別名鹏往,提高可讀性。
也可以定義一些閉包骇塘,書寫方便伊履。typealias Index = Int typealias Add = (Int) -> Void funk printNumber() -> Add { return { a in print("\(a)") } } printNumber()(2)
-
mutating
結構體和枚舉是值類型,值類型的屬性不能在方法中更改款违,如果需要在方法中修改需要在方向名前面加mutating唐瀑。struct Point { var x = 1.0 mutating func lalala() { x += 2 } }
-
defer
延遲執(zhí)行,附帶一個代碼塊插爹,在方法執(zhí)行結束前執(zhí)行該代碼塊哄辣,即時方法執(zhí)行過程中拋出錯誤也會執(zhí)行該代碼。多個defer時赠尾,執(zhí)行順序從后往前執(zhí)行力穗。func testDefer() throws { print("1") defer { print("2") } defer { print("3") } throw PrinterError.noFile defer { print("4") } } //打印1.3.2
-
required
用于初始化,required放在init前面气嫁。如果子類需要自定義初始化時当窗,必須實現required的初始化。class SuperClass { required init() { } } class SubClass: SuperClass { required init() { } }
-
lazy
lazy關鍵字的作用是在第一次使用屬性的時候才去生成寸宵,而不是在一開始就初始化好超全,按需使用。
當計算屬性的值比較耗時邓馒,或者需要外部值的依賴時嘶朱,用lazy比較合適。
lazy必須配合var使用光酣,因為let需要有個初始值疏遏。
lazy也是線程不安全的。lazy var count: Int = { var result = 0 for i in 0...100 { result += i } return result }()
-
訪問權限
private:只能在當前類里訪問
fileprivate: 當前文件里訪問
internal: 默認訪問級別救军,當前模塊或框架可以訪問财异,相當于Target級別
public: 可以被任何人訪問
open: 可以被任何人訪問,包括override和繼承// 訪問權限排序 open > public > internal > fileprivate > private
-
escaping
@escaping用來標記逃逸閉包唱遭。
在一個函數式的方法中戳寸,有一個閉包式的參數,如果這個閉包式的參數在函數中被返回出去了就是逃逸閉包拷泽,沒有被返回就是非逃逸閉包疫鹊。// 逃逸閉包 func closure(input: @escaping () -> ()) -> () -> () { return input } // 非逃逸閉包 func closure(input: () -> ()) { }
...
閉包
- 定義
閉包是一個引用類型的代碼塊袖瞻,可以用作函數的參數或者返回值,可以捕獲上下文的任何常量和變量拆吆。
- 表現形式
全局函數:都是有命名的閉包聋迎,但是不能捕獲任何值。
嵌套函數:都是有命名的閉包枣耀,并且能夠捕獲當前上下文的值霉晕。
閉包表達式:閉包表達式都是無名閉包,可以捕獲上下文的值捞奕。
- 捕獲上下文
默認情況下牺堰,閉包會捕獲附近作用域中的常量和變量,并使用強引用指向它們颅围。你可以通過一個捕獲列表來顯式指定它的捕獲行為伟葫。
捕獲列表在參數列表之前,由中括號括起來谷浅,里面是由逗號分隔的一系列表達式扒俯。一旦使用了捕獲列表奶卓,就必須使用 in 關鍵字一疯,即使省略了參數名、參數類型和返回類型夺姑。
//值捕獲
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure() // 打印 “0 10”
//引用捕獲
class SimpleClass {
var value: Int = 0
}
var x = SimpleClass()
var y = SimpleClass()
let closure = { [x] in
print(x.value, y.value)
}
x.value = 10
y.value = 10
closure() // 打印 “10 10”
-
循環(huán)引用
如果捕獲列表中的值是引用類型墩邀,你可以使用 weak 或者 unowned 來修飾它,閉包會分別用弱引用和無主引用來捕獲該值盏浙。myFunction { print(self.title) } // 以強引用捕獲 myFunction { [weak self] in print(self!.title) } // 以弱引用捕獲 myFunction { [unowned self] in print(self.title) } // 以無主引用捕獲
在捕獲列表中眉睹,也可以將任意表達式的值綁定到一個常量上。該表達式會在閉包被創(chuàng)建時進行求值废膘,閉包會按照指定的引用類型來捕獲表達式的值竹海。例如:
// 以弱引用捕獲 self.parent 并賦值給 parent
myFunction { [weak parent = self.parent] in print(parent!.title) }
運算符重載
-
定義類
struct HMPoint { var x = 0.0 var y = 0.0
}
-
重載二目運算符
func +(a: HMPoint, b: HMPoint) -> HMPoint { let c = HMPoint(x: a.x + b.x, y: a.y + b.y) return c } let va = HMPoint(x: 10, y: 12) let vb = HMPoint(x: 23, y: 8) let vc = va + vb vc.x // 33
-
重載前綴運算符
prefix func -(a:HMPoint) -> HMPoint{ return HMPoint(x: -a.x, y: -a.y) } let vz = -va vz.x //-10
-
重載后綴運算符
postfix func ++(a: HMPoint) -> HMPoint { return HMPoint(x: a.x + 1, y: a.y+1) } var vn = vz++ vn.x // 11
-
重載+=
//使用inout改變輸入參數 func +=(left: inout HMPoint, right: HMPoint) { left = left + right } vn += vz vn.x
修改運算符優(yōu)先級
使用<code>infix</code>修改優(yōu)先級
infix operator +-: AdditionPrecedence
extension HMPoint {
static func +- (left: HMPoint, right: HMPoint) -> HMPoint {
return HMPoint(x: left.x + right.x, y: left.y - right.y)
}
}
let dassa = vn +- vz
dassa.x
下標編程
通過<code>subscript</code>關鍵字可以使普通類同步通過下標訪問數據。
struct Subscript {
var number: Int?
subscript(index: Int) -> Int {
return (number ?? 1 ) * index
}
}
struct Subscript {
var number: Int?
subscript(index: Int) -> Int {
get {
return (number ?? 1) * index
}
set(newValue) {
number = newValue
}
}
}
//帶多個值的下標
struct Point {
subscript(x: Int, y: Int) -> Int {
return x + y
}
}
var p = Point()
p[1,3]
//帶n個下標
struct Path {
subscript(x: Int...) -> Int {
return x.count
}
}
var path = Path()
path[1,2,3]
//帶多維下標
struct Pad {
var xx: Int
subscript(x: Int) -> Pad {
return Pad(xx: x)
}
}
var pad = Pad(xx: 1)
pad[1][2][3].xx
設計模式
-
單例模式
class SomeManager: NSObject { static let sharedInstance = SomeManager() }
-
原型模式
protocol Cloning { func clone() -> AnyObject } class What: NSObject, Cloning { var name: String? func clone() -> AnyObject { return What(name: name??"") } }
-
工廠模式
protocol Fruit {
func description() -> String
}class Apple: Fruit { func description() -> String { return "Apple" } } class Banana:Fruit { func description() -> String { return "Banana" } } //工廠模式 protocol CreateProduct { func create() -> Fruit? func price() -> CGFloat? } class AppleFactory: CreateProduct { func create() -> Fruit? { return Apple() } func price() -> CGFloat? { return 3.00 } } class BananaFactory: CreateProduct { func create() -> Fruit? { return Banana() } func price() -> CGFloat? { return 4.00 } } enum CreateFactory { case Apple case Banana static func createFactory(fruit: CreateFactory) -> CreateProduct { switch fruit { case .Apple: return AppleFactory() case .Banana: return BananaFactory() } } } //測試代碼 let fruit = CreateFactory.createFactory(fruit: .Apple).create() print(fruit?.description() ?? "nil")
內存管理
http://www.reibang.com/p/f2f47a472947
Runtime
-
擴展屬性
extension UIView { private struct AssociatedKeys{ static var loadingViewKey:UIView? } var loadingView: UIView? { get { return objc_getAssociatedObject(self, &AssociatedKeys.loadingViewKey) as? UIView } set { if let newValue = newValue { newValue.backgroundColor = .red objc_setAssociatedObject(self, &AssociatedKeys.loadingViewKey, newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } }
-
交換方法
class TestSwizzling : NSObject { dynamic func methodOne() -> Int { return 1 } } extension TestSwizzling { //在 Objective-C 中,我們在 load() 方法進行 swizzling丐黄。但Swift不允許使用這個方法斋配。 override class func initialize() { let originalSelector = #selector(TestSwizzling.methodOne); let swizzledSelector = #selector(TestSwizzling.methodTwo); let originalMethod = class_getInstanceMethod(self, originalSelector); let swizzledMethod = class_getInstanceMethod(self, swizzledSelector); method_exchangeImplementations(originalMethod, swizzledMethod); } func methodTwo() -> Int{ // swizzling 后, 該方法就不會遞歸調用 return methodTwo() + 1 } } var c = TestSwizzling() print(c.methodOne()) //2 print(c.methodTwo()) //1
-
獲取屬性和方法名
fun allPropertyNamesAndValues(cls:AnyClass) ->[String: AnyObject] { var count: UInt32 = 0 let properties = class_copyPropertyList(cls, &count) var resultDict: [String: AnyObject] = [:] for var i = 0; i < Int(count); ++i { let property = properties[i] // 取得屬性名 let name = property_getName(property) if let propertyName = String.fromCString(name) { // 取得屬性值 if let propertyValue = self.valueForKey(propertyName) { resultDict[propertyName] = propertyValue } } } return resultDict } func allMethods() { var count: UInt32 = 0 let methods = class_copyMethodList(Person.self, &count) for var i = 0; i < Int(count); ++i { let method = methods[i] let sel = method_getName(method) let methodName = sel_getName(sel) let argument = method_getNumberOfArguments(method) print("name: (methodName), arguemtns: (argument)") } }
GCD
-
隊列
1??主隊列VS全局隊列
主隊列,即主線程灌闺,主要做UI相關操作艰争。
全局隊列,即子線程桂对,主要進行耗時操作和并行操作甩卓。獲取主線程:
DispatchQueue.main
獲取子線程:
DispatchQueue.global()
常用寫法:
DispatchQueue.global().async {
// 耗時操作
DispatchQueue.main.sync {
//UI操作
}
}2??并行隊列VS串行隊列
并行隊列:多個任務可以同時執(zhí)行
let cQueue = DispatchQueue(label: "name", attributes: .concurrent)
串行隊列:多個任務按順序執(zhí)行
let cQueue = DispatchQueue(label: "name")
3??隊列優(yōu)先級
隊列與隊列之間是并行的,可以通過設置隊列優(yōu)先級改變隊列的執(zhí)行順序蕉斜。
DispatchQueue(label: "a", qos: .background).async {
print(1)
}DispatchQueue(label: "a", qos: .default).async { print(2) } DispatchQueue(label: "a", qos: .unspecified).async { print(3) } // 打印結果 3 2 1
-
任務
1??同步任務
阻塞當前線程逾柿,任務執(zhí)行完線程才會繼續(xù)執(zhí)行
conQ.sync {
// 同步操作
}
2??異步任務
線程直接往下執(zhí)行缀棍,不阻塞當前線程
conQ.async {
// 異步操作
} 延時
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
// 延時任務
})線程安全
單元測試
同OC
第三方庫中都有很全面的單元測試代碼,可以學習一下鹿寻。
第三方庫
- SnapKit
自動布局約束庫 - SwiftyJson
將對象轉化為Json處理 - Alamofire
網絡庫 - RxSwift
函數式編程 - ObjectMapper
同SwiftyJson - Quick
行為驅動開發(fā)框架 - Eureka
快速構建 iOS 各種復雜表單的庫 - Spring
封裝的系統(tǒng)動畫庫 - Kingfisher
圖片緩存庫 - CoreStore
Core Data管理類庫