Swift Language
反射
サーチ…
構文
- ミラー(反映:インスタンス)//ミラーを初期化して、対象を反映させます
- mirror.displayStyle // Xcodeのプレイグラウンドに使用される表示スタイル
- mirror.description //このインスタンスのテキスト表現。「 CustomStringConvertible 」を参照してください。
- mirror.subjectType //反射される対象のタイプを返します
- mirror.superclassMirror //反映されているサブジェクトのスーパークラスのミラーを返します
備考
- 一般的な備考:
Mirror
は、Swiftのオブジェクトのイントロスペクションに使用されるstruct
です。その最も顕著なプロパティはchildren配列です。使用可能なケースの1つは、 Core Data
構造体をシリアル化することです。これは、 struct
をNSManagedObject
変換することによって行われます。
- ミラーの基本的な使用方法備考:
Mirror
のchildren
プロパティは、Mirrorインスタンスが反映しているオブジェクトの子オブジェクトの配列です。 child
オブジェクトには、 label
とvalue
2つのプロパティがありvalue
。例えば、子供はtitle
というtitle
とGame of Thrones: A Song of Ice and Fire
の値を持つプロパティであるかもしれません。
ミラーの基本的な使用法
ミラーの対象となるクラスを作成する
class Project {
var title: String = ""
var id: Int = 0
var platform: String = ""
var version: Int = 0
var info: String?
}
実際にミラーの対象となるインスタンスを作成する。ここでは、Projectクラスのプロパティに値を追加することもできます。
let sampleProject = Project()
sampleProject.title = "MirrorMirror"
sampleProject.id = 199
sampleProject.platform = "iOS"
sampleProject.version = 2
sampleProject.info = "test app for Reflection"
以下のコードは、Mirrorインスタンスの作成を示しています。ミラーのchildrenプロパティはAnyForwardCollection<Child>
ここで、 Child
はサブジェクトのプロパティと値のtypealiasタプルです。 Child
はlabel: String
持っていたlabel: String
と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)")
}
上のforループのXcodeでPlaygroundまたはConsoleに出力します。
title:MirrorMirror
id:199
platform:iOS
version:2
info:Optional("test app for Reflection")
Xcode 8 Beta 2でPlaygroundでテスト済み
クラスのプロパティの型と名前をインスタンス化せずに取得する
Swiftクラスの使用Mirror
は、特定のクラスのインスタンスの名前 、 値 、 型 (Swift 3: type(of: value)
、Swift 2: value.dynamicType
)をvalue.dynamicType
する場合に機能します 。
もしクラスから継承する場合はNSObject
は、メソッドを使用することができますclass_copyPropertyList
一緒property_getAttributes
そのインスタンスを持たずに - 名前とクラスのプロパティの種類を見つけるために。私はGithubのプロジェクトを作成しましたが、ここにコードがあります:
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
}
primitiveDataTypes
は、属性文字列内の文字を値型にマッピングするディクショナリです。
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
}
NSDate
(Swift3: Date
)、 NSString
(Swift3: String
?)、 NSNumber
などのNSObject
から継承するすべてのプロパティのNSObject.Type
を抽出できますが、 Any
型のストアです(メソッドによって返されたDictionaryの値の型)。これは、Int、Int32、Boolなどのvalue types
の制限に起因します。これらの型はNSObjectから継承しないので、Int- Int.self
.self
を呼び出すとNSObject.Typeが.self
れず、 Any
型が返されます。したがって、このメソッドはDictionary<String, Any>?
返しますDictionary<String, Any>?
Dictionary<String, NSObject.Type>?
なくDictionary<String, NSObject.Type>?
。
このメソッドは次のように使用できます。
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'
また、 Any
をNSObject.Type
にキャストしようとすることもできますNSObject
、 NSObject
から継承したすべてのプロパティで成功します。次に、標準==
演算子を使用して型をチェックできます。
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'")
}
}
}
}
このカスタム==
演算子を宣言した場合:
func ==(rhs: Any, lhs: Any) -> Bool {
let rhsType: String = "\(rhs)"
let lhsType: String = "\(lhs)"
let same = rhsType == lhsType
return same
}
このようにvalue types
の型の型をチェックすることさえできます:
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'")
}
}
}
制限事項このソリューションは、 value types
がオプションである場合は機能しません。 NSObjectのサブクラスで次のようにプロパティを宣言した場合: var myOptionalInt: Int?
上記のコードでは、 class_copyPropertyList
メソッドにオプションの値型が含まれていないため、そのプロパティは見つかりません。