Swift 4中如何实现NSObject的不区分大小写版responds(to: Selector)?
嘿,这个场景我之前做推送映射的时候也遇到过!刚好可以给你几个在Swift 4里实现不区分大小写检查NSObject属性/方法的方案,亲测好用~
方案1:扩展NSObject实现不区分大小写的
responds(to:)替代方法 这个方法会遍历当前类及父类的所有实例方法,通过将方法名转小写后和目标字符串对比,实现不区分大小写的匹配:
import Foundation extension NSObject { func respondsToSelectorIgnoringCase(_ selectorName: String) -> Bool { let lowercaseTarget = selectorName.lowercased() var methodCount: UInt32 = 0 guard let methods = class_copyMethodList(type(of: self), &methodCount) else { return false } defer { free(methods) // 记得释放Runtime分配的内存 } // 遍历当前类的所有方法 for i in 0..<Int(methodCount) { let method = methods[i] let selector = method_getName(method) let selectorStr = String(cString: sel_getName(selector)).lowercased() if selectorStr == lowercaseTarget { return true } } // 当前类没找到的话,递归检查父类 if let superclass = class_getSuperclass(type(of: self)) { guard let superInstance = superclass.init() as? NSObject else { return false } return superInstance.respondsToSelectorIgnoringCase(lowercaseTarget) } return false } }
方案2:针对属性的精准检查(如果只关心属性而非所有方法)
如果你的需求只是检查属性(而不是所有Objective-C方法),可以用class_copyPropertyList直接遍历属性列表,这样更精准:
extension NSObject { func hasPropertyIgnoringCase(_ propertyName: String) -> Bool { let lowercaseTarget = propertyName.lowercased() var propertyCount: UInt32 = 0 guard let properties = class_copyPropertyList(type(of: self), &propertyCount) else { return false } defer { free(properties) } // 遍历当前类的所有属性 for i in 0..<Int(propertyCount) { let property = properties[i] let propNameStr = String(cString: property_getName(property)).lowercased() if propNameStr == lowercaseTarget { return true } } // 检查父类属性 if let superclass = class_getSuperclass(type(of: self)) { guard let superInstance = superclass.init() as? NSObject else { return false } return superInstance.hasPropertyIgnoringCase(lowercaseTarget) } return false } }
额外补充:获取匹配的Selector以便实际调用
如果检查通过后你还需要调用对应的方法/属性,可以扩展一个方法返回匹配的Selector:
extension NSObject { func selectorForNameIgnoringCase(_ name: String) -> Selector? { let lowercaseTarget = name.lowercased() var methodCount: UInt32 = 0 guard let methods = class_copyMethodList(type(of: self), &methodCount) else { return nil } defer { free(methods) } for i in 0..<Int(methodCount) { let method = methods[i] let selector = method_getName(method) let selectorStr = String(cString: sel_getName(selector)).lowercased() if selectorStr == lowercaseTarget { return selector } } // 检查父类 if let superclass = class_getSuperclass(type(of: self)) { guard let superInstance = superclass.init() as? NSObject else { return nil } return superInstance.selectorForNameIgnoringCase(lowercaseTarget) } return nil } }
使用示例
假设你有一个User类:
class User: NSObject { @objc var userName: String = "Jack" @objc var age: Int = 28 } let user = User() // 检查是否存在小写的"username"对应的属性 print(user.respondsToSelectorIgnoringCase("username")) // 输出 true print(user.hasPropertyIgnoringCase("age")) // 输出 true // 获取Selector并调用属性 if let userNameSelector = user.selectorForNameIgnoringCase("username") { let userName = user.perform(userNameSelector)?.takeUnretainedValue() as? String print(userName) // 输出 "Jack" }
注意事项
- 所有需要被检测的属性/方法必须加上
@objc修饰,因为Swift的Runtime只能访问暴露给Objective-C的成员。 - 如果存在多个转小写后名字相同的方法/属性,会返回第一个匹配的结果(不过驼峰命名规范下这种情况很少见)。
- 递归检查父类的时候,我们创建了父类的实例,对于一些需要初始化参数的父类,可能需要调整逻辑(不过NSObject的子类大多支持无参初始化)。
内容的提问来源于stack exchange,提问作者Blackbeard
相关产品推荐
相关产品推荐

