Remarque: une question similaire a été posée pour l’objectif C sur ici , mais je souhaite y parvenir dans Swift.
J'ai une classe déclarée dans Swift comme ceci:
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)
}
}
Maintenant, je veux obtenir un tableau dans Swift enlisting: dateDisplayLabel, nameDisplayLabel.
Comment puis-je atteindre cet objectif?
Mirror
Voici une solution Swift pure avec quelques limitations:
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"]
Limites:
Ne renverra pas les propriétés calculées, c'est-à-dire:
var favoriteFood: String { return "Lemon Cake" }
Si self
est une instance d'une classe (vs une struct, par exemple), cela ne rapporte pas les propriétés de sa superclasse,
class Person : PropertyNames {
var name = "Bruce Wayne"
}
class Superhero : Person {
var hasSuperpowers = true
}
Superhero().propertyNames() // ["hasSuperpowers"] — no "name"
Vous pouvez contourner ce problème en utilisant superclassMirror()
en fonction de votre comportement souhaité.
class_copyPropertyList
Si vous utilisez des objets Objective-C, vous pouvez utiliser cette approche:
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)
La sortie sur la console si classToInspect
est NSURL
:
["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"]
Cela ne fonctionnera pas dans un terrain de jeu. Il suffit de remplacer NSURL
par EachDayCell
(ou de réutiliser la même logique comme une extension) et cela devrait fonctionner.
Voici une autre version.Je pense que c'est beaucoup simple et pur.
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()
J'ai converti le code bolivia en Swift 4. Cette fonction prend un objet NSObject et renvoie un dictionnaire des clés de l'objet et du type de cette clé.
Notez que les types sont un peu moche. Pour les propriétés primitives, le moteur renvoie un identificateur d'une lettre (par exemple, B
pour bool, i
pour int, etc.), mais pour les types Obj-C, il renvoie des éléments tels que @"NSString"
. Voir que c'est vraiment juste une fonction de débogage pour moi ne me dérangeait pas. Si vous ne voulez pas manipuler le dictionnaire, vous pouvez simplement supprimer le commentaire de la ligne print
et le vider dans la console. String(cString:cAttr)
contient également de nombreuses informations utiles, notamment si la propriété est modifiable, son style de référence, etc. Pour plus d'informations à ce sujet, voici la documentation d'Apple.
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;
}
Swift 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)
}