Swift高级语法
主要总结一些平时遇到的疑难点,在此总结出来,持续更新。可能有些误导大家的地方,欢迎指正。
难点
-
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中的属性没有对应的实例变量,必须自己创建。
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>。
观察器观察的不止是对象本身,对象的某个属性的值改变也会触发观察器。
⚠️设置观察期后就不能实现计算属性和存储属性。
- 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》中提出的一种通过写方法模板来快速实现方法的机制。让我们的方法更加动态化,可以整合系统方法。
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
这是一个非常好用的关键字,用来定义别名。系统使用了大量别名。
可以定义一些有意义的别名,提高可读性。
也可以定义一些闭包,书写方便。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
延迟执行,附带一个代码块,在方法执行结束前执行该代码块,即时方法执行过程中抛出错误也会执行该代码。多个defer时,执行顺序从后往前执行。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”
-
循环引用
如果捕获列表中的值是引用类型,你可以使用 weak 或者 unowned 来修饰它,闭包会分别用弱引用和无主引用来捕获该值。myFunction { print(self.title) } // 以强引用捕获 myFunction { [weak self] in print(self!.title) } // 以弱引用捕获 myFunction { [unowned self] in print(self.title) } // 以无主引用捕获
在捕获列表中,也可以将任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如:
// 以弱引用捕获 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
-
修改运算符优先级
使用<code>infix</code>修改优先级
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.jianshu.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串行队列
并行队列:多个任务可以同时执行
let cQueue = DispatchQueue(label: "name", attributes: .concurrent)
串行队列:多个任务按顺序执行
let cQueue = DispatchQueue(label: "name")
3️⃣队列优先级
队列与队列之间是并行的,可以通过设置队列优先级改变队列的执行顺序。
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️⃣同步任务
阻塞当前线程,任务执行完线程才会继续执行
conQ.sync {
// 同步操作
}
2️⃣异步任务
线程直接往下执行,不阻塞当前线程
conQ.async {
// 异步操作
} -
延时
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
// 延时任务
}) -
线程安全
单元测试
同OC
第三方库中都有很全面的单元测试代码,可以学习一下。
第三方库
- SnapKit
自动布局约束库 - SwiftyJson
将对象转化为Json处理 - Alamofire
网络库 - RxSwift
函数式编程 - ObjectMapper
同SwiftyJson - Quick
行为驱动开发框架 - Eureka
快速构建 iOS 各种复杂表单的库 - Spring
封装的系统动画库 - Kingfisher
图片缓存库 - CoreStore
Core Data管理类库