只能被class继承的协议
- 被
@objc
修饰的协议,还可以暴露给OC
去遵守实现
protocol Runnable : AnyObject {}
protocol Runnable : class {}
@objc protocol Runnable: {}
可选协议
- 可以通过
@objc
定义可选协议,这种协议只能被class
遵守
@objc protocol Runnable {
func run1()
@objc optional func run2()//可选协议
func run3()
}
class HomeViewController: UIViewController, Runnable {
func run1() {
}
func run3() {
}
}
dynamic
- 被
@objc dynamic
修饰的内容会具有动态性,比如调用方法会走runtime
那一套流程
class Dog: NSObject {
@objc dynamic func test1(){}//runTime流程
func test2() { }//虚表
}
KVC \ KVO
-
Swift
支持KVC\KVO
的条件
- 属性所在的类,监听器最终继承自
NSObject
- 用
@objc dynamic
修饰对应的属性
class Observer: NSObject {
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("observeValue",change?[.oldKey] ?? (Any).self)
print("observeValue",change?[.newKey] ?? (Any).self)
}
}
class Person: NSObject {
@objc dynamic var age: String = "0"
var observer: Observer = Observer()
override init() {
super.init()
self.addObserver(observer, forKeyPath: "age", options: [.new,.old], context: nil)
}
deinit {
self.removeObserver(observer, forKeyPath: "age")
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
let p = Person()
p.age = "20"
p.age = "30"
p.setValue("25", forKey: "age")
}
}
//打印结果
observeValue 0
observeValue 20
observeValue 20
observeValue 30
observeValue 30
observeValue 25
block方式的KVO
class Person: NSObject {
@objc dynamic var age: Int = 0
var observation: NSKeyValueObservation?
override init() {
super.init()
observation = observe(\Person.age, options: .new, changeHandler: { (_, change) in
print(change.newValue as Any)
})
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
let p = Person()
p.age = 10
}
}
关联对象(Associated Object)
- 在
Swift
中,class
依然可以使用关联对象
- 默认情况
extension
不可以增加存储属性
- 借助关联对象,可以实现类似
extension
为class
增加存储属性的效果
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)
}
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
let p = Person()
print(p.age)//0
p.age = 10;
print(p.age)//10
}
}
资源名管理
let img = UIImage(named: "logo")
let btn = UIButton(type: .custom)
btn.setTitle("添加", for: .normal)
performSegue(withIdentifier: "login_main", sender: self)
- 这种做法实际上参考了
Android
的资源名管理方式
enum R {
enum string: String {
case add = "添加"
}
enum image: String {
case logo
}
enum segue: String {
case login_main
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
let img = UIImage(named: R.image.logo.rawValue)
let btn = UIButton(type: .custom)
btn.setTitle(R.string.add.rawValue, for: .normal)
performSegue(withIdentifier: R.segue.login_main.rawValue, sender: self)
}
}
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 UIViewController {
func performSegue(withIdentifier identifier: R.segue, sender: Any?){
performSegue(withIdentifier: identifier.rawValue, sender: sender)
}
}
extension UIButton {
func setTitle(_ title: R.string, for state: UIControl.State) {
setTitle(title.rawValue, for: state)
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
let img = UIImage.init(R.image.logo)
let btn = UIButton(type: .custom)
btn.setTitle(R.string.add, for: .normal)
performSegue(withIdentifier: R.segue.login_main, sender: self)
}
}
enum R {
enum image {
static var logo = UIImage(named: "logo")
}
enum font {
static func arial(_ size: CGFloat) -> UIFont? {
UIFont(name: "Arial", size: size)
}
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
let img = R.image.logo
let font = R.font.arial(14)
}
}