如何检查对象是不是具有存储属性?
Posted
技术标签:
【中文标题】如何检查对象是不是具有存储属性?【英文标题】:How to check if an object has a stored property?如何检查对象是否具有存储属性? 【发布时间】:2016-02-14 22:42:44 【问题描述】:我需要检查一个对象的类是否有一个可用于赋值的成员。假设有一个类:
class MyClass: NSObject
var myVar : AnyObject!
// ...
我尝试了这种 objC 样式,但属性在 Swift 中是不同的野兽,第二行总是返回 false:
let myClass: MyClass = MyClass()
let hasClassMember = myClass.respondsToSelector(Selector("setMyVar:"))
if hasClassMember
myClass.performSelector("setMyVar:", withObject: "Context" as! AnyObject)
我认为 Swift 没有像 ObjC 那样强大的反射,但是有什么解决方案吗?
更新 事实证明我的代码可以工作,但是为了演示目的我已经对其进行了简化。奇怪的是,一旦我将 myVar 的类型更改为某个自定义类的实例,例如 ClassB,responsToSelector 就会停止工作。感觉有点神奇……:)
UPDATE2终于找到了问题所在。还没写完整代码,以为问题出在其他地方,反正这里是完整代码。 MyClass 变量实际上是其他一些实现了 AnotherClassProtocol 的 AnotherClass 的类型
protocol AnotherClassProtocol
//…
class AnotherClass: AnotherClassProtocol
//…
class MyClass: NSObject
var myVar : AnotherClass!
// ...
这导致 myClass.respondsToSelector(Selector("setMyVar:")) 总是返回 false。事实证明,问题是因为我在 AnotherClass 声明中省略了 NSObject 的扩展。在我修复它之后,它开始按预期工作:
class AnotherClass: NSObject, AnotherClassProtocol
//…
我仍在学习 Swift,并认为不需要 NSObject,因为编译器没有抱怨,而且一切都从 NSObject 扩展而来(至少在 ObjC 中)。好吧,我很高兴我发现了这个讨厌的错误。
【问题讨论】:
我在操场上尝试了这段代码,它对我来说工作正常。我不得不删除 as! AnyObject 和我还删除了setMyVar:
周围的selector()
,因为它不需要。 hasClassMember
也是基于属性名称的真/假
【参考方案1】:
比检查选择器更好的方法是使用协议。
protocol MyClassProtocol
var myVar : AnyObject! get set
class MyClass: NSObject, MyClassProtocol
var myVar : AnyObject!
// ...
...
let c = MyClass()
if let conformingMyClass = c as? MyClassProtocol // returns true
print("yay")
conformingMyClass.myVar = "Context"
else
print("nay")
这样一来,哪些类拥有和不拥有你的属性就更明确了,而且你不必纠结于选择器(更多的是 Swift-y)。
【讨论】:
嗯,这种方法似乎很难与 TDD 一起使用,您应该经常在实现变量之前对其进行测试,不是吗?【参考方案2】:import Foundation
class MyClass: NSObject
var myVar1 : AnyObject!
// ...
let myClass: MyClass = MyClass()
let hasClassMemberMyVar1 = myClass.respondsToSelector(Selector("setMyVar1:")) // true
let hasClassMemberMyVar2 = myClass.respondsToSelector(Selector("setMyVar2:")) // false
它对我有用...
更新,基于 OP 说明
import Foundation
class C:NSObject
class MyClass: NSObject
var myVar1 : C? // Objective-C representable
var i: Int = 0 // Objective-C representable
var j: Int? = 10
let myClass: MyClass = MyClass()
let hasClassMemberMyVar1 = myClass.respondsToSelector(Selector("setMyVar1:")) // true
let hasClassMemberMyVar2 = myClass.respondsToSelector(Selector("setMyVar2:")) // false
let hasClassMemberI = myClass.respondsToSelector(Selector("setI:")) // true
let hasClassMemberJ = myClass.respondsToSelector(Selector("setJ:")) // false, because Optional<Int> is not representable in Objective-C !!!
print(myClass.i.dynamicType, myClass.j.dynamicType) // Int Optional<Int>
仅具有类类型属性
import Foundation
class C:NSObject
class C1
class MyClass: NSObject
var c : C?
var cO1: C = C()
var cO2: C!
var c1: C1 = C1()
var c2: C1?
var c3: C1!
let myClass: MyClass = MyClass()
let hasClassMemberC = myClass.respondsToSelector(Selector("setC:")) // true
let hasClassMemberCO1 = myClass.respondsToSelector(Selector("setCO1:")) // true
let hasClassMemberCO2 = myClass.respondsToSelector(Selector("setCO2:")) // true
let hasClassMemberC1 = myClass.respondsToSelector(Selector("setC1:")) // false, class C1 is not Objective-C representable ...
let hasClassMemberC2 = myClass.respondsToSelector(Selector("setC2:")) // false, Optional<C1> is not Objective-C representable ...
let hasClassMemberC3 = myClass.respondsToSelector(Selector("setC3:")) // false, ImplicitlyUnwrappedOptional<C1> is not Objective-C representable ...
【讨论】:
看起来它正在使用 myVar 作为 AnyObject 类型,但是如果我改变它的类型,它就不会。请检查我的更新。 @Centurion 如果 myVar1 的类型继承自 NSObject,它应该仍然可以工作。请注意,Selector 仅适用于动态绑定,因此您的类属性也应该是 Objective-C 可表示的。 @Centurion 我添加了所有属性都是某个类(引用类型)的示例 谢谢,请检查更新的问题。你是对的,它应该可以工作,但我犯了一个错误。【参考方案3】:Ryan Huubert 回答的 Swift 3 版本:
class MyClass: NSObject
var myVar : AnyObject!
// ...
let myClass = MyClass()
myClass.responds(to: Selector("myVar")) // returns true
myClass.responds(to: Selector("myVar:")) // returns false
【讨论】:
【参考方案4】:class MyClass: NSObject
var myVar : AnyObject!
// ...
let myClass = MyClass()
myClass.respondsToSelector("myVar") // returns true
myClass.respondsToSelector("myVar:") // returns false
【讨论】:
以上是关于如何检查对象是不是具有存储属性?的主要内容,如果未能解决你的问题,请参考以下文章