选择器(Selector)
Swift中依然可以使用选择器,使用#selector(name)定义一个选择器
必须是被@objcMembers或@objc修饰的方法才可以定义选择器
perform(@Selector)和 @selector()本来就是Runtime,OC的。
没有添加@objc会提示:没有暴漏给OC的方法名是不能使用的
下面两例分别是perform(@Selector)和 @selector()
@objcMembers class Temp: NSObject {
func test1(v1: Int) { print("test1") }
func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") }
func test2(_ v1: Double, _ v2: Double) { print("test2(_:_:)") }
func run() {
perform(#selector(test1))
perform(#selector(test1(v1:)))
perform(#selector(test2(v1:v2:)))
perform(#selector(test2(_:_:)))
perform(#selector(test2 as (Double, Double) -> Void))
}
}
var temp = Temp()
temp.run()
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
view.addSubview(button)
button.setTitle("测试", for: .normal)
button.layer.borderWidth = 1
button.layer.borderColor = RD_RandomColor().cgColor
button.backgroundColor = RD_RandomColor()
button.addTarget(self, action: #selector(btnMethod), for: UIControl.Event.touchUpInside)
button.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(CGSizeMake(200, 100))
}
}
@objc func btnMethod() {
print("点击了测试按钮")
}
@objc
1 OC调用swift,使用@objc修饰swift文件的属性或方法,选择性暴露给OC。
2 OC调用swift,可以通过 @objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等)
3 被 @objc 修饰的协议,还可以暴露给OC去遵守实现
4 可以通过 @objc 定义可选协议(这种协议只能被 class 遵守)
5 必须是被@objcMembers或@objc修饰的方法才可以定义选择器。
我们给button,手势,添加方法。也要用到@objc.原理同1,没有暴漏给OC的方法名是不能使用的。
perform(@Selector)和 @selector()本来就是Runtime,OC的。
只能被class继承的协议
方法一:protocol Runnable1: AnyObject {}
方法二:protocol Runnable2: class {}
方法三:@objc protocol Runnable3 {}
可选协议
可以通过 @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() {}
}
KVC\KVO
Swift 支持 KVC \ KVO 的条件
1 属性所在的类、监听器最终继承自 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 = .init()
override init() {
super.init()
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
class Person: NSObject {
@objc dynamic var age: Int = 0
var observation: NSKeyValueObservation?
override init() {
super.init()
observation = observe(\Person.age, options: .new) {
_, change in
print(change.newValue as Any)
}
}
}
var p = Person()
// observeValue Optional(20)
p.age = 20
// observeValue Optional(25)
p.setValue(25, forKey: "age")
关联对象(Associated Object)
在Swift中,class依然可以使用关联对象
默认情况,extension不可以增加存储属性。借助关联对象,可以实现类似extension为class增加存储属性的效果
objc_getAssociatedObject是Foundation内的接口,纯Swift开发需要导入。
KEY建议用Void?或者BOOL类型,只用一个字节。因为我们只需要一个唯一的地址值做key。
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思路
方式一:我们通过枚举来管理各种资源的名称,再通过扩展设置需要的方法。
typealias AM = AssertsManager
enum AssertsManager {
enum string: String {
case add = "添加"
}
enum image: String {
case logo = "pic"
}
enum segue: String{
case login_main
}
}
extension UIImage {
convenience init?(_ name: AssertsManager.image) {
self.init(named: name.rawValue)
}
}
extension UIViewController {
func performSegue(withIdentifier identifier: AssertsManager.segue, sender: Any?) {
performSegue(withIdentifier: identifier.rawValue, sender: sender)
}
}
extension UIButton {
func setTitle(_ title: AssertsManager.string, for state: UIControl.State) {
setTitle(title.rawValue, for: state)
}
}
struct temp{
let img = UIImage(AssertsManager.image.logo)
let btn = UIButton(type: .custom)
}
btn.setTitle(R.string.add, for: .normal)
performSegue(withIdentifier: R.segue.login_main, sender: self)
方法二:
typealias AM = AssertsManager
enum AssertsManager {
enum image {
static var logo = UIImage(named: "tab4_select")
}
enum font {
static func arial(_ size: CGFloat) -> UIFont? {
UIFont(name: "Arial", size: size)
}
}
}
let imgView = UIImageView.init(frame: CGRect(x: 100, y: 200, width: 100, height: 100))
imgView.image = AM.image.logo
view.addSubview(imgView)
/* 使用示例
let img = AM.image.logo //let img = UIImage(named: "logo")
let font = AM.font.arial(14) //let font = UIFont(name: "Arial", size: 14)
*/
更多优秀开源参考
https://github.com/mac-cain13/R.swift
https://github.com/SwiftGen/SwiftGen