只能被class(类)继承的协议
- 只能被class(类)继承的协议有三种:
- 继承自AnyObject
- 继承自class
- 被@objc修饰的类
protocol Runnable1: AnyObject { }
protocol Runnable2: class { }
@objc protocol Runnable3 { }
struct Animal: Runnable1 { } //error: Non-class type 'Animal' cannot conform to class protocol 'Runnable1'
struct Animal2: Runnable2 { } //error: Non-class type 'Animal' cannot conform to class protocol 'Runnable1'
struct Animal3: Runnable3 { } //error: Non-class type 'Animal' cannot conform to class protocol 'Runnable1'
class Student: Runnable1 { }
class Student2: Runnable2 { }
class Student3: Runnable3 { }
- 被@objc修饰的协议,还可以暴露给OC去遵守实现
可选协议
- 可以通过@objc定义可选协议,这种协议只能被class(类)遵守
@objc protocol Runnable {
func run1()
//可选方法,可实现,可不实现
@objc optional func run2()
func run3()
}
class Dog: Runnable {
func run3() { print("Dog run3") }
func run1() { print("Dog run1") }
}
var d = Dog()
d.run1() //Dog run1
d.run3() //Dog run3
dynamic
- 被@objc dynamic修饰的内容会具有动态性,比如调用方法会走runtime那一套
class Dog: NSObject {
@objc dynamic func test1 () { }
func test2() { }
}
var d = Dog()
d.test1()
d.test2()
- test1会走runtime的objc_msgSend,底层汇编代码如下:
- test2会走Swift的callq虚表那一套,底层汇编代码如下:
KVC\KVO
- Swift支持KVC\KVO的条件
- 属性所在的类、监听器最终继承自NSObject,因为KVC\KVO走的是runtime那一套,所以要继承NSObject
- 用@objc dynamic修饰对应的属性
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()
p.age = 20 //observeValue Optional(20)
p.setValue(25, forKey: "age") //observeValue Optional(25)
- block方式的KVO
class Person: NSObject {
@objc dynamic var age: Int = 10
var observation: NSKeyValueObservation?
override init() {
super.init()
observation = observe(\Person.age
, options:.new, changeHandler: { (_, change) in
print(change.newValue as Any)
})
}
}
var p = Person()
p.age = 20 //Optional(20)
p.setValue(25, forKey: "age") //Optional(25)
关联对象(Associated Object)
- 在Swift中, class依然可以使用关联对象
- 默认情况,extension不可以增加存储属性
- 借助关联对象,可以实现类似extension为class增加存储属性的效果
示例1:
class Person { }
extension Person {
private static var AGE_KEY: Void? //类属性相当于暴露在外面的全局变量,地址是固定的,这里可以看到AGE_KEY是Void?类型,占一个字节,也可以是bool类型,也占一个字节,这样做是为了节省内存
var age: Int {
get {
// return (objc_getAssociatedObject(self, &Person.AGE_KEY) as? Int) ?? 0
return objc_getAssociatedObject(self, &Person.AGE_KEY) as! Int
}
set {
objc_setAssociatedObject(self, &Person.AGE_KEY, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN)
}
}
}
var p = Person()
p.age = 0
print(p.age) //0
p.age = 10
print(p.age) //10
示例2:
- 关联对象怎么存储呢?最底层有一个哈希表(map),在表里会有一个存储对象信息的字典,关联对象时首先会传入一个对象,根据对象找到对应的value,value又是类似于字典一样的东西,再传入第二个参数一个固定的地址,根据地址就可以存储和获得所需要的属性值
资源名管理
- 如果有太多需要调用名字为logo的图片或设置名字为添加的button,上面的写法一次次写未免太麻烦,所以要用到资源名管理,写法如下:
- 第一种写法
注:case logo相当于case logo = "logo"
2)第二种写法
- 更多优秀的思路参考:
多线程开发 - 异步
- Asyncs.swift
- DispatchWorkItem的notify是当异步线程执行完后通知主线程调用
import Foundation
public typealias Task = () -> Void
public struct Asyncs {
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是当异步线程执行完后通知主线程调用
item.notify(queue: DispatchQueue.main, execute: main)
}
}
}
- ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Asyncs.async({ print(1)}) //1
Asyncs.async({
print(1, Thread.current) //1 <NSThread: 0x60000027f640>{number = 3, name = (null)}
}) {
print(2, Thread.current) //2 <NSThread: 0x604000261740>{number = 1, name = main}
}
}
}
多线程开发 - 延迟
- Asyncs.swift
import Foundation
public typealias Task = () -> Void
public struct Asyncs {
//返回值可以忽略,当你没用到返回值的时候,也不会报警告⚠️
@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
}
@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;
}
}
- ViewController.swift
import UIKit
class ViewController: UIViewController {
private var item: DispatchWorkItem?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
item = Asyncs.delay(3) {
print(1) //1
}
item = Asyncs.asyncDelay(3, {
print(1, Thread.current) //1 <NSThread: 0x60400046e9c0>{number = 3, name = (null)}
}, {
print(2, Thread.current) //2 <NSThread: 0x60000007be80>{number = 1, name = main}
})
}
override func touchesBegan(_ touches: Set<UITouch>, with: UIEvent?) {
item?.cancel() //取消操作
}
}
多线程开发 - once
dispatch_once在Swift中已被废弃,取而代之
- 可以用类型属性或者全局变量\常量
static修饰的类型属性示例:
import UIKit
class ViewController: UIViewController {
static var age: Int = getAge()
static func getAge() -> Int {
print("getAge")
return 0
}
override func viewDidLoad() {
super.viewDidLoad()
print(ViewController.age)
print(ViewController.age)
print(ViewController.age)
}
}
//输出结果
//getAge
//0
//0
//0
全局变量(默认lazy)示例:
import UIKit
//lazy
fileprivate var initTask: Void = {
print("init-----")
}()
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
let _ = initTask
let _ = initTask
let _ = initTask
}
//输出结果
//init-----
//可以看到只调用了一次
2. 默认自带 lazy + dispatch_once 效果
多线程开发 - 加锁
- gcd信号量
private static var lock = DispatchSemaphore(value: 1) 的(value: 1)代表在同一时刻,只有一条线程可访问,不能多条线程同时访问
- Foundation
这是一般锁,不能针对递归调用,可能依然会遇到因递归造成的同时调用的问题而报错:
下面的是递归锁,能解决递归调用问题: