【问题标题】:List of class's properties in swiftswift中类的属性列表
【发布时间】:2014-09-10 18:04:41
【问题描述】:

注意:在here 上发布了针对目标 c 的类似问题,但我想快速实现。

我有一个像这样在 swift 中声明的类:

import UIKit

class EachDayCell : UITableViewCell
{

    @IBOutlet var dateDisplayLabel : UITextField
    @IBOutlet var nameDisplayLabel : UITextField

    @IBAction func goToPendingItems(sender : AnyObject) {
    }
    @IBAction func showDateSelectionPicker(sender : AnyObject) {
    }

    init(style: UITableViewCellStyle, reuseIdentifier: String!)
    {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }
}

现在我想在 swift enlisting 中获取一个数组:dateDisplayLabel、nameDisplayLabel。

我怎样才能做到这一点?

【问题讨论】:

    标签: class properties swift


    【解决方案1】:

    使用Mirror

    这是一个纯 Swift 解决方案,但有一些限制:

    protocol PropertyNames {
        func propertyNames() -> [String]
    }
    
    extension PropertyNames
    {
        func propertyNames() -> [String] {
            return Mirror(reflecting: self).children.flatMap { $0.label }
        }
    }
    
    class Person : PropertyNames {
        var name = "Sansa Stark"
        var awesome = true
    }
    
    Person().propertyNames() // ["name", "awesome"]
    

    限制:

    • 为 Objective-C 对象返回一个空数组
    • 不会返回计算属性,即:

      var favoriteFood: String { return "Lemon Cake" }
      
    • 如果self 是类的实例(相对于结构),则不会报告其超类的属性,即:

      class Person : PropertyNames {
          var name = "Bruce Wayne"
      }
      
      class Superhero : Person {
          var hasSuperpowers = true
      }
      
      Superhero().propertyNames() // ["hasSuperpowers"] — no "name"
      

      您可以使用 superclassMirror() 解决此问题,具体取决于您想要的行为。

    使用class_copyPropertyList

    如果您使用的是 Objective-C 对象,您可以使用这种方法:

    var count = UInt32()
    let classToInspect = NSURL.self
    let properties : UnsafeMutablePointer <objc_property_t> = class_copyPropertyList(classToInspect, &count)
    var propertyNames = [String]()
    let intCount = Int(count)
    for var i = 0; i < intCount; i++ {
        let property : objc_property_t = properties[i]
        guard let propertyName = NSString(UTF8String: property_getName(property)) as? String else {
            debugPrint("Couldn't unwrap property name for \(property)")
            break
        }
    
        propertyNames.append(propertyName)
    }
    
    free(properties)
    print(propertyNames)
    

    如果classToInspectNSURL,则输出到控制台:

    ["pathComponents", "lastPathComponent", "pathExtension", "URLByDeletingLastPathComponent", "URLByDeletingPathExtension", "URLByStandardizingPath", "URLByResolvingSymlinksInPath", "dataRepresentation", "absoluteString", "relativeString", "baseURL", "absoluteURL", "scheme", "resourceSpecifier", "host", "port", "user", "password", "path", "fragment", "parameterString", "query", "relativePath", "hasDirectoryPath", "fileSystemRepresentation", "fileURL", "standardizedURL", "filePathURL"]

    这在操场上是行不通的。只需将NSURL 替换为EachDayCell(或重复使用与扩展相同的逻辑),它应该可以工作。

    【讨论】:

    • @AaronBrager 快速评论。您说如果self 是一个类,您将不会获得超类属性。但是在您的示例中,self 永远不是类,因为 1)propertyKeys 不是类方法,因此 self 将始终引用一个实例,并且 2)您在 Superhero().propertyKeys() 而不是 Superhero.propertyKeys() 上调用它跨度>
    • @startupthekid 我不是在谈论实例与类属性。我说的是self 何时是类的实例(相对于结构)
    • 这种方法不适用于 Int、Double 和 swift 的其他数据类型。
    • classToInspect 在使用 class_copyPropertyList 时必须是 NSObject
    • 有没有办法在不实例化类的情况下获取属性名称和类型?
    【解决方案2】:

    这是另一个版本。我认为这是非常简单和纯粹的。

    Swift 2.0

    protocol Reflectable {
      func properties()->[String]
    }
    
    extension Reflectable
    {
        func properties()->[String]{
            var s = [String]()
            for c in Mirror(reflecting: self).children
            {
                if let name = c.label{
                    s.append(name)
                }
            }
            return s
        }
    }
    
    
    class Test:Reflectable
    {
        var name99:String = ""
        var name3:String = ""
        var name2:String = ""
    }
    
    Test().properties()
    

    Swift 1.2

    class Reflect:NSObject {
        func properties()->[String]
        {
            let m = reflect(self)
            var s = [String]()
            for i in 0..<m.count
            {
                let (name,_)  = m[i]
                if name == "super"{continue}
                s.append(name)
            }
            return s
        }
    }
    
    class Test:Reflect
    {
        var name99:String = ""
        var name3:String = ""
        var name2:String = ""
    }
    
    Test().properties()
    

    【讨论】:

    • 对于未来的围观者:这个答案也可以很容易地返回属性的值
    【解决方案3】:

    我将bolivia's 代码转换为 Swift 4。此函数接受一个 NSObject 并返回对象键的字典以及该键的类型

    请注意,这些类型有点难看。对于原始属性,引擎返回一个单字母标识符(例如 B 用于 bool,i 用于 int 等),但对于 Obj-C 类型,它返回类似 @"NSString" 的内容。看到这对我来说实际上只是一个调试功能,我不介意。如果您不想弄乱字典,您可以取消注释 print 行并将其转储到控制台。 String(cString:cAttr) 还包含很多有用的信息,包括属性是否可变、它的参考样式等等。 For more info on this here's Apple's documentation.

    func getKeysAndTypes(forObject:Any?) -> Dictionary<String,String> {
            var answer:Dictionary<String,String> = [:]
            var counts = UInt32()
            let properties = class_copyPropertyList(object_getClass(forObject), &counts)
            for i in 0..<counts {
                let property = properties?.advanced(by: Int(i)).pointee
                let cName = property_getName(property!)
                let name = String(cString: cName)
                
                let cAttr = property_getAttributes(property!)!
                let attr = String(cString:cAttr).components(separatedBy: ",")[0].replacingOccurrences(of: "T", with: "")
                answer[name] = attr
                //print("ID: \(property.unsafelyUnwrapped.debugDescription): Name \(name), Attr: \(attr)")
            }
            return answer
        }
    

    【讨论】:

    • 比较代码:为什么在返回之前省略了free(properties)
    【解决方案4】:

    斯威夫特 3.1

    let contorller = UIActivityViewController(activityItems: [url], applicationActivities: nil)
    var count: UInt32 = 0
    guard let properties = class_copyPropertyList(object_getClass(contorller), &count) else {
        return
    }
    for index in 0...count {
        let property1 = property_getName(properties[Int(index)])
        let result1 = String(cString: property1!)
        print(result1)
    }
    

    【讨论】:

      【解决方案5】:

      您也可以像这样在NSObject 上进行扩展:

      extension NSObject {
          var properties: [Mirror.Child] {
              Mirror(reflecting: self).children.compactMap { $0 }
          }
      
          var propertyNames: [String] {
              properties.compactMap { $0.label }
          }
      
          var propertyValues: [Any] {
              properties.map { $0.value }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-26
        • 1970-01-01
        • 1970-01-01
        • 2014-11-04
        • 2013-11-06
        • 1970-01-01
        • 2016-11-10
        相关资源
        最近更新 更多