Swift Language
Reflexion
Sök…
Syntax
- Spegel (reflekterande: instans) // Initierar en spegel med motivet att reflektera
- mirror.displayStyle // Displaystil som används för Xcode-lekplatser
- mirror.description // Textrepresentation av den här instansen, se CustomStringConvertible
- mirror.subjectType // Returnerar typen av motiv som reflekteras
- mirror.superclassMirror // Returnerar spegeln för superklassen för motivet som reflekteras
Anmärkningar
- Allmänna kommentarer:
En Mirror
är en struct
används i introspektionen av ett objekt i Swift. Den mest framstående egenskapen är barngruppen. Ett möjligt användningsfall är att serialisera en struktur för Core Data
. Detta görs genom att konvertera en struct
till ett NSManagedObject
.
- Grundläggande användning för spegelanmärkningar:
Den children
egenskap hos en Mirror
är en array med underordnade objekt från objektet Mirror instans reflekterar. Ett child
objekt har två egenskaper label
och value
. Till exempel kan ett barn vara en egenskap med namnet title
och värdet av Game of Thrones: A Song of Ice and Fire
.
Grundläggande användning för spegel
Att skapa klassen för att vara ämnet för spegeln
class Project {
var title: String = ""
var id: Int = 0
var platform: String = ""
var version: Int = 0
var info: String?
}
Skapa en instans som faktiskt kommer att bli föremål för spegeln. Här kan du också lägga till värden i egenskaperna för projektklassen.
let sampleProject = Project()
sampleProject.title = "MirrorMirror"
sampleProject.id = 199
sampleProject.platform = "iOS"
sampleProject.version = 2
sampleProject.info = "test app for Reflection"
Koden nedan visar skapandet av Mirror-instansen. Spegelns egendom för barn är en AnyForwardCollection<Child>
där Child
är typealias-tupel för motivets egendom och värde. Child
hade en label: String
och value: Any
.
let projectMirror = Mirror(reflecting: sampleProject)
let properties = projectMirror.children
print(properties.count) //5
print(properties.first?.label) //Optional("title")
print(properties.first!.value) //MirrorMirror
print()
for property in properties {
print("\(property.label!):\(property.value)")
}
Output i Playground eller Console i Xcode för for-loopen ovan.
title:MirrorMirror
id:199
platform:iOS
version:2
info:Optional("test app for Reflection")
Testat i Playground på Xcode 8 beta 2
Få typ och namn på egenskaper för en klass utan att behöva instansera det
Användning av Swift-klassen Mirror
fungerar om du vill extrahera namn , värde och typ (Swift 3: type(of: value)
, Swift 2: value.dynamicType
) för egenskaper för en instans av en viss klass.
Om du klassar arv från NSObject
kan du använda metoden class_copyPropertyList
tillsammans med property_getAttributes
att ta reda på namnet och typerna av egenskaper för en klass - utan att ha en instans av den . Jag skapade ett projekt på Github för detta, men här är koden:
func getTypesOfProperties(in clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}
func getTypeOf(property: objc_property_t) -> Any {
guard let attributesAsNSString: NSString = NSString(utf8String: property_getAttributes(property)) else { return Any.self }
let attributes = attributesAsNSString as String
let slices = attributes.components(separatedBy: "\"")
guard slices.count > 1 else { return getPrimitiveDataType(withAttributes: attributes) }
let objectClassName = slices[1]
let objectClass = NSClassFromString(objectClassName) as! NSObject.Type
return objectClass
}
func getPrimitiveDataType(withAttributes attributes: String) -> Any {
guard let letter = attributes.substring(from: 1, to: 2), let type = primitiveDataTypes[letter] else { return Any.self }
return type
}
Där primitiveDataTypes
är en ordbok som mappar en bokstav i attributsträngen till en värdetyp:
let primitiveDataTypes: Dictionary<String, Any> = [
"c" : Int8.self,
"s" : Int16.self,
"i" : Int32.self,
"q" : Int.self, //also: Int64, NSInteger, only true on 64 bit platforms
"S" : UInt16.self,
"I" : UInt32.self,
"Q" : UInt.self, //also UInt64, only true on 64 bit platforms
"B" : Bool.self,
"d" : Double.self,
"f" : Float.self,
"{" : Decimal.self
]
func getNameOf(property: objc_property_t) -> String? {
guard let name: NSString = NSString(utf8String: property_getName(property)) else { return nil }
return name as String
}
Det kan extrahera NSObject.Type
av alla egenskaper som NSObject
ärver från NSObject
som NSDate
(Swift3: Date
), NSString
(Swift3: String
?) Och NSNumber
, men det är lagrat i typen Any
(som du kan se som typ av värdet på ordlistan som returneras med metoden). Detta beror på begränsningarna av value types
som Int, Int32, Bool. Eftersom dessa typer inte ärver från NSObject .self
på t.ex. en Int - Int.self
NSObject.Type utan snarare typen Any
. Således returnerar metoden Dictionary<String, Any>?
och inte Dictionary<String, NSObject.Type>?
.
Du kan använda den här metoden så här:
class Book: NSObject {
let title: String
let author: String?
let numberOfPages: Int
let released: Date
let isPocket: Bool
init(title: String, author: String?, numberOfPages: Int, released: Date, isPocket: Bool) {
self.title = title
self.author = author
self.numberOfPages = numberOfPages
self.released = released
self.isPocket = isPocket
}
}
guard let types = getTypesOfProperties(in: Book.self) else { return }
for (name, type) in types {
print("'\(name)' has type '\(type)'")
}
// Prints:
// 'title' has type 'NSString'
// 'numberOfPages' has type 'Int'
// 'author' has type 'NSString'
// 'released' has type 'NSDate'
// 'isPocket' has type 'Bool'
Du kan också försöka kasta Any
till NSObject.Type
, som lyckas för alla egenskaper som ärver från NSObject
, då kan du kontrollera typen med standard ==
operator:
func checkPropertiesOfBook() {
guard let types = getTypesOfProperties(in: Book.self) else { return }
for (name, type) in types {
if let objectType = type as? NSObject.Type {
if objectType == NSDate.self {
print("Property named '\(name)' has type 'NSDate'")
} else if objectType == NSString.self {
print("Property named '\(name)' has type 'NSString'")
}
}
}
}
Om du förklarar den här anpassade operatören ==
:
func ==(rhs: Any, lhs: Any) -> Bool {
let rhsType: String = "\(rhs)"
let lhsType: String = "\(lhs)"
let same = rhsType == lhsType
return same
}
Du kan till och med kontrollera vilken typ av value types
som denna:
func checkPropertiesOfBook() {
guard let types = getTypesOfProperties(in: Book.self) else { return }
for (name, type) in types {
if type == Int.self {
print("Property named '\(name)' has type 'Int'")
} else if type == Bool.self {
print("Property named '\(name)' has type 'Bool'")
}
}
}
BEGRÄNSNINGAR Denna lösning fungerar inte när value types
är tillval. Om du har deklarerat en egenskap i din NSObject-underklass så här: var myOptionalInt: Int?
, koden ovan hittar inte den egenskapen eftersom metoden class_copyPropertyList
inte innehåller valfria värdetyper.