如何知道对象是否是子类或超类的实例

时间:2015-12-28 03:16:35

标签: ios swift

我有2个班级:

class Parent
{
  func a() {
     self.b()
  }
  func b() {
     // I want to check here
     if self is Parent  // warning "'is' test is always true"
     {
        log("instance of parent")
     }
  }
}
class Child:Parent
{
}

我想像这样检查

//
var child = Child()
child.a()  // don't see log
var parent = Parent()
parent.a()  // see log

我知道我可以在超类中创建类似description的方法,并在子类中覆盖它。 我想知道Swift是否可以在没有实现description

的情况下检查它

感谢您的帮助

4 个答案:

答案 0 :(得分:9)

非常简单,使用is关键字。

if child is Child

答案 1 :(得分:5)

这可以使用as类型强制转换运算符来完成:

var child = Child()

if let child = child as? Child {
    //you know child is a Child
} else if let parent = child as? Parent {
    //you know child is a Parent
}

还有is关键字:

if child is Child {
    //is a child
}

请注意,在您的代码中,您会看到使用is的警告 - 它始终是真的,因为从类中比较selfParent Parent始终为true。如果您将其与某个类的其他实例进行比较而不是self,或者如果您要与Parent之外的其他类型进行比较,则此警告将消失。

我建议在iBooks商店的Swift Programming Language一书中阅读更多相关内容 - 请参阅Type Casting一章。

答案 2 :(得分:3)

您收到警告,因为该函数已附加到其定义的类中。因此,编译器已经提前知道self的类型。但是你的设计根本不是一个好的设计。例如,让我们在父级上定义printMe函数:

class Parent
{
  func printMe() {
    if self is Parent {
        print("Parent")
    }
  }
}

class Child: Parent {}

let child = Child()
child.printMe() // Output: Parent

好的,Child类继承了printMe的{​​{1}},所以让我们覆盖它:

Parent

您必须为每个子类重新定义class Child: Parent { func printMe() { if self is Child { print("Child") } } } 。在每种情况下,编译器已经知道该函数属于哪个类,那么为什么还要进行printMe测试?

正确的设计模式是使用协议:

is

答案 3 :(得分:0)

答案应该更简单,以便处理意外错误。即。也许您添加了一个新的子类,但是却忘记更新switch语句,因此错误地打印了Parent

我在上面给出的快速示例不应该打印任何内容,也不应该打印Unexpected Class之类的东西,以便使所有内容保持真实,一致和面向未来。似乎不费吹灰之力就检查布尔值child is Child,但您必须及时进行仔细的检查,因为is关键字对于子类总是返回true(因此,问题中提到的警告做'is' test is always true时发生self is Parent

这是我的解决方法:

class Parent {

    // MARK: Main method

    func printClass() {
        print(className)
    }

    // MARK: Helper methods

    var isParent: Bool {
        return type(of: self) === Parent.self
    }

    static var className: String {
        return String(describing: self)
    }

    var className: String {
        return type(of: self).className
    }

    // MARK: Implementation to recognize Parent, Child and any other Subclass

    // Approach #1: Identify if "self" == "Parent"
    func printClassOrSubclass() {
        guard isParent else {
            // Case #1: Class of "self" != "Parent", so "self" must be a subclass of "Parent"
            print("\(className), subclass of Parent")
            return
        }

        // Case #2: Class of "self" == "Parent", its not a subclass
        printClass()
    }

    // Approach #2: Identify what class is "self" exactly. This approach is useful for calling a method or do something with "self" as a subclass instance (ie. "child == self as! Child")
    func printClassOrSubclassWithDowncasting() {
        guard !isParent else {
            // Case #1: Class of "self" == "Parent", so "self" is not a subclass of "Parent"
            printClass()
            return
        }

        // Switch #1: Attempt to identify the exact class of "self", which is definitely a subclass of "Parent" by now.
        switch self {
        case let child as Child where className == Child.className:
            // Case #2: Class of "self" == "Child"
            child.printChildClass()

            // In each case of Switch #1, returning is important so we dont continue to the Switch #2 by accident
            return
        default:
            // Switch #1 failed to identify the exact class of self. Code will continue with Switch #2
            break
        }

        // Switch #2: Attempt to identify if "self" is a subclass of a subclass of Parent (aka, subclass of "Child")
        switch self {
        case let subChild as Child:
            // Case #3: Class of "self" != "Child" but is a subclass of "Child". (Maybe a different dev introduced "GrandChild" Class and forgot to tell you, classic)

            print("\(className), unexpected subclass of Child")
            subChild.printChildClass()
        default:
            // Case #4: Class of "self" could not be identified at all, but its still a subclass of "Parent". (Maybe marketing is testing an "Uncle" Class?)

            print("\(className), unexpected subclass of Parent")
            break
        }
    }
}

class Child: Parent {
    func printChildClass() {
        print("\(className), subclass of \(String(describing: class_getSuperclass(type(of: self))!))")
    }
}

// Unexpected Subclasses
class GrandChild: Child {}
class Uncle: Parent {}

测试:

let parent = Parent()
parent.printClassOrSubclass()
parent.printClassOrSubclassWithDowncasting()
print("\n")

let child = Child()
child.printClassOrSubclass()
child.printClassOrSubclassWithDowncasting()
print("\n")

let grandChild = GrandChild()
grandChild.printClassOrSubclass()
grandChild.printClassOrSubclassWithDowncasting()
print("\n")

let uncle = Uncle()
uncle.printClassOrSubclass()
uncle.printClassOrSubclassWithDowncasting()

结果:

Parent
Parent

Child, subclass of Parent
Child, subclass of Parent

GrandChild, subclass of Parent
GrandChild, unexpected subclass of Child
GrandChild, subclass of Child

Uncle, subclass of Parent
Uncle, unexpected subclass of Parent

注意:

我的确很简单,但是我想针对大多数情况做出彻底的回应。也就是说,您可以针对特定用例调整/简化代码。同样,2个switch语句也可以合并为1,但是您必须以完美的顺序安排switch的情况(在检查子类之前,必须始终检查确切的类),否则您将得到错误的结果。对于我的答案,我在考虑多个开发人员可能正在处理和添加新的类和子类。因此,从长远来看,两个开关应该鼓励具有更好的结构和更少的混乱。