Swift:从OC到Swift

只能被class(类)继承的协议

  • 只能被class(类)继承的协议有三种:
  1. 继承自AnyObject
  2. 继承自class
  3. 被@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的条件
  1. 属性所在的类、监听器最终继承自NSObject,因为KVC\KVO走的是runtime那一套,所以要继承NSObject
  2. 用@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,上面的写法一次次写未免太麻烦,所以要用到资源名管理,写法如下:
  1. 第一种写法

 注:case logo相当于case logo = "logo"

2)第二种写法

  • 更多优秀的思路参考:
  1. https://github.com/mac-cain13/R.swift
  2. https://github.com/SwiftGen/SwiftGen

多线程开发 - 异步

  • 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中已被废弃,取而代之

  1. 可以用类型属性或者全局变量\常量

    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

这是一般锁,不能针对递归调用,可能依然会遇到因递归造成的同时调用的问题而报错:

下面的是递归锁,能解决递归调用问题:

猜你喜欢

转载自blog.csdn.net/weixin_42433480/article/details/99070899