KVC/KVO

  • Swift 支持KVC/KVO的条件
    • 属性所在的类、监听器最终继承自 NSObject
    • 用 @objc dynamic 修饰对应的属性

KVO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 监听器
class Observer: NSObject {
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
print("observeValue", change?[.newKey] as Any)
}
}

// 类
class Person: NSObject {
@objc dynamic var age: Int = 0
var observer: Observer = Observer()
override init() {
super.init()
self.addObserver(observer,
forKeyPath: "age",
options: .new,
context: nil)
}
deinit {
self.removeObserver(observer,
forKeyPath: "age")
}
}

var p = Person()
// observeValue Optional(20)
p.age = 20
// observeValue Optional(25)
p.setValue(25, forKey: "age")

block方式的KVO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person: NSObject {
@objc dynamic var age: Int = 0
var observation: NSKeyValueObservation?
override init() {
super.init()
observation = observe(\Person.age, options: .new) {
(person, change) in
print(change.newValue as Any)
}
}
}

var p = Person()
// Optional(20)
p.age = 20
// Optional(25)
p.setValue(25, forKey: "age")

关联对象(Associated Object)

  • 在Swift中,class依然可以使用关联对象
    • 默认情况,extension不可以增加存储属性
    • 借助关联对象,可以实现类似extension为class增加存储属性的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {}

extension Person {
private static var AGE_KEY: Void?
var age: Int {
get {(objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0 }
set {
objc_setAssociatedObject(self,
&Self.AGE_KEY,
newValue,
.OBJC_ASSOCIATION_ASSIGN)
}
}
}

var p = Person()
print(p.age) // 0
p.age = 10
print(p.age) // 10

资源名管理

  • 参考Android的资源名管理方式

改善前

1
2
3
4
5
6
let img = UIImage(named: "logo")

let btn = UIButton(type: .custom)
btn.setTitle("添加", for: .normal)

performSegue(withIdentifier: "login_main", sender: self)

改善后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//枚举封装
enum R {
enum string: String {
case add = "添加"
}
enum image: String {
case logo
}
enum segue: String {
case login_main
}
}

// 扩展
extension UIImage {
convenience init?(_ name: R.image) {
self.init(named: name.rawValue)
}
}

extension UIButton {
func setTitle(_ title: R.string, for state: UIControl.State) {
setTitle(title.rawValue, for: state)
}
}

extension UIViewController {
func performSegue(withIdentifier identifier: R.segue, sender: Any?) {
performSegue(withIdentifier: identifier.rawValue, sender: sender)
}
}

// 使用
let img = UIImage(R.image.logo)

let btn = UIButton(type: .custom)
btn.setTitle(R.string.add, for: .normal)

performSegue(withIdentifier: R.segue.login_main, sender: self)

资源名管理的其他思路

优化前

1
2
3
let img = UIImage(named: "logo")

let font = UIFont(name: "Arial", size: 14)

优化后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//枚举进行封装
enum R {
enum image {
static var logo = UIImage(named: "logo")
}
enum font {
static func arial(_ size: CGFloat) -> UIFont? {
UIFont(name: "Arial", size: size)
}
}
}

let img = R.image.logo

let font = R.font.arial(14)

多线程开发 - 异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public typealias Task = () -> Void

struct Async {
//子线程开辟任务
public static func async(_ task: @escaping Task) {
_async(task)
}

//子线程任务完成之后,回到主线程执行任务
public static func async(_ task: @escaping Task, _ mainTask: @escaping Task) {
_async(task, mainTask)
}

private static func _async(_ task: @escaping Task,
_ mainTask: Task? = nil) {
let item = DispatchWorkItem(block: task)
DispatchQueue.global().async(execute: item)
if let main = mainTask {
item.notify(queue: DispatchQueue.main, execute: main)
}
}
}

多线程开发 - 异步延迟

一般用法

1
2
3
4
5
6
7
@discardableResult //忽略返回值
public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem {
let item = DispatchWorkItem(block: block)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds,
execute: item)
return item
}

封装使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@discardableResult
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task) -> DispatchWorkItem {
return _asyncDelay(seconds, task)
}

@discardableResult
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: @escaping Task) -> DispatchWorkItem {
return _asyncDelay(seconds, task, mainTask)
}

private static func _asyncDelay(_ seconds: Double,
_ task: @escaping Task,
_ mainTask: Task? = nil) -> DispatchWorkItem {
let item = DispatchWorkItem(block: task)
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
execute: item)
if let main = mainTask {
item.notify(queue: DispatchQueue.main, execute: main)
}
return item
}

多线程开发 - once

  • dispatch_once在Swift中已被废弃,取而代之
    • 以用类型属性或者全局变量\常量
    • 默认自带 lazy + dispatch_once 效果
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      fileprivate let initTask2: Void = {
      print("initTask2---------")
      }()

      class ViewController: UIViewController {
      static let initTask1: Void = {
      print("initTask1---------")
      }()

      override func viewDidLoad() {
      super.viewDidLoad()

      let _ = Self.initTask1

      let _ = initTask2
      }
      }

多线程开发 - 加锁

  • gcd信号量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Cache {
    private static var data = [String: Any]()
    private static var lock = DispatchSemaphore(value: 1)
    static func set(_ key: String, _ value: Any) {
    lock.wait()
    defer { lock.signal() }
    data[key] = value
    }
    }
  • Foundation
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //正常加锁
    private static var lock = NSLock()
    static func set(_ key: String, _ value: Any) {
    lock.lock()
    defer { lock.unlock() }
    }

    // 递归锁
    private static var lock = NSRecursiveLock()
    static func set(_ key: String, _ value: Any) {
    lock.lock()
    defer { lock.unlock() }
    }

参考

  • 李明杰老师课件