如何检查对象是不是具有存储属性?

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

【讨论】:

以上是关于如何检查对象是不是具有存储属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查对象是不是在 JavaScript 中具有特定属性?

如何检查包含另一个对象数组的对象数组是不是具有属性

检查对象是不是具有属性[重复]

如何检查对象数组是不是具有重复的属性值?

如何检查两个对象是不是具有相同的一组属性名称?

如何在 C# 中快速检查两个数据传输对象是不是具有相同的属性?