【问题标题】:How iterate through all properties of different classes in Swift?如何遍历 Swift 中不同类的所有属性?
【发布时间】:2018-01-28 23:06:30
【问题描述】:

假设我有一个名为 Car 的父类,它具有一些属性和一个实际上什么都不做的方法。

class Car {
    var brandName : String
    var yearOfCreation : Int
    var color : String
    var numberOfDoors : Int

    func manipulateWithCar() {
    }

    init(brandName : String, yearOfCreation : Int, color : String, numberOfDoors : Int) {
        self.brandName = brandName
        self.yearOfCreation = yearOfCreation
        self.color = color
        self.numberOfDoors = numberOfDoors
    }
}

我有两个子类,称为 SportCar 和 TankerCar。它们有其独特的属性,并且父类的方法也被覆盖,现在它做了一些事情。

class SportCar : Car {
    var turbo : Bool
    var nitrousOxideEngine : Bool

    override func manipulateWithCar() {
        print("Putting turbo on your car...")
        turbo = true
    }

    init(brandName: String, yearOfCreation: Int, color: String, numberOfDoors: Int, turbo : Bool, nitrousOxideEngine : Bool) {
        self.turbo = turbo
        self.nitrousOxideEngine = nitrousOxideEngine
        super.init(brandName: brandName, yearOfCreation: yearOfCreation, color: color, numberOfDoors: numberOfDoors)
    }
}

该方法还有一个枚举。这并不重要,但你只想把它放在作业中。

enum TankerForm {
    case cilindrical
    case quadratic
}

class TankerCar : Car{
    var tankerFilledSize : Int
    var tankerForm : TankerForm

    init(brandName : String, yearOfCreation : Int,
         color : String, numberOfDoors : Int, tankerVolum : Int, tankerForm : TankerForm) {
        self.tankerForm = tankerForm
        self.tankerFilledSize = tankerVolum
        super.init(brandName: brandName, yearOfCreation: yearOfCreation, color: color, numberOfDoors: numberOfDoors)
    }

    override func manipulateWithCar() {
        print("Loading 200 gallon of oil")
        tankerFilledSize = 200
    }
}

创建一些对象...

var kia = Car(brandName: "Rio", yearOfCreation: 2015, color: "Green", 
numberOfDoors: 4)

var toyota = Car(brandName: "Corolla", yearOfCreation: 2015, color: 
"Red", numberOfDoors: 4)

var lamborgini = SportCar(brandName: "Aventador", yearOfCreation: 2018, 
color: "Black", numberOfDoors: 2, turbo: false, nitrousOxideEngine: 
false)

var mazeratti = SportCar(brandName: "Ghibli", yearOfCreation: 2017, 
color: "Red", numberOfDoors: 4, turbo: true, nitrousOxideEngine: false)

var kamaz = TankerCar(brandName: "Kamaz", yearOfCreation: 2007, color: 
"Yellow", numberOfDoors: 2, tankerVolum: 0, tankerForm: .cilindrical)

var belaz = TankerCar(brandName: "Belaz", yearOfCreation: 2003, color: 
"White", numberOfDoors: 2, tankerVolum: 0, tankerForm: .quadratic)

用对象创建数组...

var carrsArray : [Car] = [kia, toyota]
var sportCarsArray : [SportCar] = [lamborgini, mazeratti]
var tankerCarsArray : [TankerCar] = [kamaz, belaz]

并使用 for 循环打印 Car 类对象的所有属性...

for value in carrsArray {
    print("This car is \(value.brandName)")
    print("The color is \(value.color)")
    print("The car has \(value.numberOfDoors) doors")
    print("The year of creation is \(value.yearOfCreation)")
    print(" ")
}

使用 for 循环打印 SportCar 类对象的所有属性...

for value in sportCarsArray {
    print("This car is \(value.brandName)")
    print("The color is \(value.color)")
    print("The car has \(value.numberOfDoors) doors")
    print("The year of creation is \(value.yearOfCreation)")
    if value.nitrousOxideEngine == true {
        print("This car has Nitrous Oxide Engine")
    } else {
         print("There is no Nitrous Oxide Engine on this car.")
    }
    if value.turbo == true {
        print("This car has turbo")
    } else {
        print("There is no turbo in this car.")
    }
    print(" ")
}

使用 for 循环打印 TankerCar 类对象的所有属性...

for value in tankerCarsArray {
    print("This car is \(value.brandName)")
    print("The color is \(value.color)")
    print("The car has \(value.numberOfDoors) doors")
    print("The year of creation is \(value.yearOfCreation)")
    print("The tanker is filled with \(value.tankerFilledSize) lb of oil.")

    if value.tankerForm == .cilindrical {
        print("This car has cilindrical tanker form.")
    } else {
        print("This car has quadratic tanker form.")
    }
    print(" ")
}

现在是问题。有没有办法打印不同类的所有属性?我尝试创建一个包含三个数组的新数组:

var allClassesArrays = [carrsArray, sportCarsArray, tankerCarsArray]

但是在迭代过程中,它能够获取到父类的所有属性。

【问题讨论】:

  • 也许使用镜像? Mirror
  • 您可以遍历carrsArray + sportCarsArray + tankerCarsArray(使用map 向上转换[SportsCar] 和[TankerCar] 到[Car],如有必要,idr),但您在迭代期间的元素类型将是Car .如果您想要 SportsCar 和 TankerCar 特定属性,则需要在循环体中向下转换。
  • 另一种解决方案是创建Car: CustomStringConvertible,并让每个Car 子类定义它如何将自身转换为字符串。然后在您的循环中,您只需访问description,并让动态调度自动为您解析正确的转换。

标签: arrays swift swift4


【解决方案1】:

这就是反射的用途。您可以向Car 类添加一个属性/方法,该类列出当前类的所有属性,以及超类的属性。当这个属性/方法被动态应用到子类时,它会给出你想要的。

我们需要对Mirror 的扩展,它以递归方式返回当前实例及其超类的所有属性,取自Martin R's answer:

extension Mirror {
    func toDictionary() -> [String: Any] {
        var dict = [String: Any]()

        // Attributes of this class
        for attr in self.children {
            if let propertyName = attr.label {
                dict[propertyName] = attr.value
            }
        }

        // Attributes of superclass
        if let superclass = self.superclassMirror {
            for (propertyName, value) in superclass.toDictionary() {
                dict[propertyName] = value
            }
        }

        return dict
    }
}

class Car: CustomDebugStringConvertible {
    // ...

    var debugDescription: String {
        let dict = Mirror(reflecting: self).toDictionary()
        return dict.map { "\($0.key) = \($0.value)" }.joined(separator: "\n")
    }
}

用法:

var allClassesArrays = [carrsArray, sportCarsArray, tankerCarsArray]
for car in allClassesArrays.flatMap({ $0 }) {
    print(car.debugDescription, terminator: "\n\n")
}

您当然不必将Car 与CustomDebugStringConvertible 一致或将属性命名为debugDescription。我只是觉得它们很方便,因为当您键入 po car 时,调试器将使用 debugDescription。

【讨论】:

    猜你喜欢
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 2015-07-23
    • 1970-01-01
    • 2010-10-24
    • 2015-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多