获取Swift中的数组元素类型(通过反射)
Get a type of Element of an array in Swift (through reflection)
假设我有以下代码
class Foo {
}
var fooArray : Array<Foo> = Array<Foo>()
// This is important because in my code I will get Any (vs Array<Foo)
var fooArrayAny : Any = foo
我希望能够从变量 fooArrayAny 中得到类型 Foo。
如果我有 fooArray,我会这样做:
let type = fooArray.dynamicType.Element().dynamicType
但是,这不适用于 fooArrayAny。它说它没有成员 Element()
class Foo {
var foo: Int = 1
}
struct Boo {
var boo: String = "alfa"
}
func f(array: Any) {
let mirror = Mirror(reflecting: array)
let arraytype = mirror.subjectType
switch arraytype {
case is Array<Foo>.Type:
let fooArray = array as! Array<Foo>
print(fooArray)
case is Array<Boo>.Type:
let booArray = array as! Array<Boo>
print(booArray)
default:
print("array is not Array<Foo> nor Array<Boo>")
break
}
}
var fooArray : Array<Foo> = []
fooArray.append(Foo())
var anyArray : Any = fooArray // cast as Any
f(anyArray) // [Foo]
var booArray : Array<Boo> = []
booArray.append(Boo())
anyArray = booArray // cast as Any
f(anyArray) // [Boo(boo: "alfa")]
var intArray : Array<Int> = []
anyArray = intArray // cast as Any
f(anyArray) // array is not Array<Foo> nor Array<Boo>
如果您将 NSObject 设置为 Foo 的基础 class,那么您可以使用以下代码:
class EVReflectionTests: XCTestCase {
func testArrayInstance() {
let fooArray : Array<Foo> = Array<Foo>()
let fooArrayAny : Any = fooArray
if let arr = fooArray as? Array {
let i = arr.getArrayTypeInstance(arr)
print("i = \(i)")
}
}
}
class Foo: NSObject {
}
extension Array {
public func getArrayTypeInstance<T>(arr:Array<T>) -> T {
return arr.getTypeInstance()
}
public func getTypeInstance<T>() -> T {
let nsobjectype : NSObject.Type = T.self as! NSObject.Type
let nsobject: NSObject = nsobjectype.init()
return nsobject as! T
}
}
这段代码是我的库的一个片段EVReflection
更新:
我注意到上面的代码中有一个错误。我使用 fooArray 而不是 fooArrayAny。将其更改为 fooArrayAny 时,我得到与您相同的错误,即编译器没有元素。在尝试了这个之后,我发现了一个可行的解决方案。它再次包含我的 EVReflection 库的部分代码。
class EVReflectionTests: XCTestCase {
func testArrayInstance() {
let fooArray : Array<Foo> = Array<Foo>()
let fooArrayAny : Any = fooArray
if let _ = fooArrayAny as? NSArray {
var subtype: String = "\(Mirror(reflecting: fooArrayAny))"
subtype = subtype.substringFromIndex((subtype.componentsSeparatedByString("<") [0] + "<").endIndex)
subtype = subtype.substringToIndex(subtype.endIndex.predecessor())
print("The type of the array elements = \(subtype)")
if let instance = swiftClassFromString(subtype) {
print("An instance of the array element = \(instance)")
let type = instance.dynamicType
print("An instance of the array element = \(type)")
}
}
}
// All code below is a copy from the EVReflection library.
func swiftClassFromString(className: String) -> NSObject? {
var result: NSObject? = nil
if className == "NSObject" {
return NSObject()
}
if let anyobjectype : AnyObject.Type = swiftClassTypeFromString(className) {
if let nsobjectype : NSObject.Type = anyobjectype as? NSObject.Type {
let nsobject: NSObject = nsobjectype.init()
result = nsobject
}
}
return result
}
func swiftClassTypeFromString(className: String) -> AnyClass! {
if className.hasPrefix("_Tt") {
return NSClassFromString(className)
}
var classStringName = className
if className.rangeOfString(".", options: NSStringCompareOptions.CaseInsensitiveSearch) == nil {
let appName = getCleanAppName()
classStringName = "\(appName).\(className)"
}
return NSClassFromString(classStringName)
}
func getCleanAppName(forObject: NSObject? = nil)-> String {
var bundle = NSBundle.mainBundle()
if forObject != nil {
bundle = NSBundle(forClass: forObject!.dynamicType)
}
var appName = bundle.infoDictionary?["CFBundleName"] as? String ?? ""
if appName == "" {
if bundle.bundleIdentifier == nil {
bundle = NSBundle(forClass: EVReflection().dynamicType)
}
appName = (bundle.bundleIdentifier!).characters.split(isSeparator: {[=11=] == "."}).map({ String([=11=]) }).last ?? ""
}
let cleanAppName = appName
.stringByReplacingOccurrencesOfString(" ", withString: "_", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
.stringByReplacingOccurrencesOfString("-", withString: "_", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
return cleanAppName
}
}
class Foo: NSObject {
}
此代码的输出将是:
The type of the array elements = Foo
An instance of the array element = <EVReflection_iOS_Tests.Foo: 0x7fd6c20173d0>
An instance of the array element = Foo
Swift 5
它很旧,但如果有人需要,我想分享我的版本。
我使用 ModelProtocol
,我建议你使用协议,这样我们就可以通过协议进行操作来建模(例如:静态实例化)。
protocol ModelProtocol {}
class Foo: ModelProtocol {
}
因为我无法检查类型是否为数组,所以我使用 CollectionProtocol
并创建 Array
扩展以通过协议获取 Element
。
protocol CollectionProtocol {
static func getElement() -> Any.Type
}
extension Array: CollectionProtocol {
static func getElement() -> Any.Type {
return Element.self
}
}
测试中。
var fooArray: Array<Foo> = Array<Foo>()
var fooArrayAny: Any = fooArray
let arrayMirrorType = type(of: fooArrayAny)
String(describing: "arrayMirrorType: \(arrayMirrorType)")
if arrayMirrorType is CollectionProtocol.Type {
let collectionType = arrayMirrorType as! CollectionProtocol.Type
String(describing: "collectionType: \(collectionType)")
let elementType = collectionType.getElement()
String(describing: "elementType: \(elementType)")
let modelType = elementType as! ModelProtocol.Type
String(describing: "modelType: \(modelType)")
// ... now you can do operation to modelType via ModelProtocol
}
正在打印。
arrayMirrorType: Array<Foo>
collectionType: Array<Foo>
elementType: Foo
modelType: Foo
假设我有以下代码
class Foo {
}
var fooArray : Array<Foo> = Array<Foo>()
// This is important because in my code I will get Any (vs Array<Foo)
var fooArrayAny : Any = foo
我希望能够从变量 fooArrayAny 中得到类型 Foo。
如果我有 fooArray,我会这样做:
let type = fooArray.dynamicType.Element().dynamicType
但是,这不适用于 fooArrayAny。它说它没有成员 Element()
class Foo {
var foo: Int = 1
}
struct Boo {
var boo: String = "alfa"
}
func f(array: Any) {
let mirror = Mirror(reflecting: array)
let arraytype = mirror.subjectType
switch arraytype {
case is Array<Foo>.Type:
let fooArray = array as! Array<Foo>
print(fooArray)
case is Array<Boo>.Type:
let booArray = array as! Array<Boo>
print(booArray)
default:
print("array is not Array<Foo> nor Array<Boo>")
break
}
}
var fooArray : Array<Foo> = []
fooArray.append(Foo())
var anyArray : Any = fooArray // cast as Any
f(anyArray) // [Foo]
var booArray : Array<Boo> = []
booArray.append(Boo())
anyArray = booArray // cast as Any
f(anyArray) // [Boo(boo: "alfa")]
var intArray : Array<Int> = []
anyArray = intArray // cast as Any
f(anyArray) // array is not Array<Foo> nor Array<Boo>
如果您将 NSObject 设置为 Foo 的基础 class,那么您可以使用以下代码:
class EVReflectionTests: XCTestCase {
func testArrayInstance() {
let fooArray : Array<Foo> = Array<Foo>()
let fooArrayAny : Any = fooArray
if let arr = fooArray as? Array {
let i = arr.getArrayTypeInstance(arr)
print("i = \(i)")
}
}
}
class Foo: NSObject {
}
extension Array {
public func getArrayTypeInstance<T>(arr:Array<T>) -> T {
return arr.getTypeInstance()
}
public func getTypeInstance<T>() -> T {
let nsobjectype : NSObject.Type = T.self as! NSObject.Type
let nsobject: NSObject = nsobjectype.init()
return nsobject as! T
}
}
这段代码是我的库的一个片段EVReflection
更新:
我注意到上面的代码中有一个错误。我使用 fooArray 而不是 fooArrayAny。将其更改为 fooArrayAny 时,我得到与您相同的错误,即编译器没有元素。在尝试了这个之后,我发现了一个可行的解决方案。它再次包含我的 EVReflection 库的部分代码。
class EVReflectionTests: XCTestCase {
func testArrayInstance() {
let fooArray : Array<Foo> = Array<Foo>()
let fooArrayAny : Any = fooArray
if let _ = fooArrayAny as? NSArray {
var subtype: String = "\(Mirror(reflecting: fooArrayAny))"
subtype = subtype.substringFromIndex((subtype.componentsSeparatedByString("<") [0] + "<").endIndex)
subtype = subtype.substringToIndex(subtype.endIndex.predecessor())
print("The type of the array elements = \(subtype)")
if let instance = swiftClassFromString(subtype) {
print("An instance of the array element = \(instance)")
let type = instance.dynamicType
print("An instance of the array element = \(type)")
}
}
}
// All code below is a copy from the EVReflection library.
func swiftClassFromString(className: String) -> NSObject? {
var result: NSObject? = nil
if className == "NSObject" {
return NSObject()
}
if let anyobjectype : AnyObject.Type = swiftClassTypeFromString(className) {
if let nsobjectype : NSObject.Type = anyobjectype as? NSObject.Type {
let nsobject: NSObject = nsobjectype.init()
result = nsobject
}
}
return result
}
func swiftClassTypeFromString(className: String) -> AnyClass! {
if className.hasPrefix("_Tt") {
return NSClassFromString(className)
}
var classStringName = className
if className.rangeOfString(".", options: NSStringCompareOptions.CaseInsensitiveSearch) == nil {
let appName = getCleanAppName()
classStringName = "\(appName).\(className)"
}
return NSClassFromString(classStringName)
}
func getCleanAppName(forObject: NSObject? = nil)-> String {
var bundle = NSBundle.mainBundle()
if forObject != nil {
bundle = NSBundle(forClass: forObject!.dynamicType)
}
var appName = bundle.infoDictionary?["CFBundleName"] as? String ?? ""
if appName == "" {
if bundle.bundleIdentifier == nil {
bundle = NSBundle(forClass: EVReflection().dynamicType)
}
appName = (bundle.bundleIdentifier!).characters.split(isSeparator: {[=11=] == "."}).map({ String([=11=]) }).last ?? ""
}
let cleanAppName = appName
.stringByReplacingOccurrencesOfString(" ", withString: "_", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
.stringByReplacingOccurrencesOfString("-", withString: "_", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
return cleanAppName
}
}
class Foo: NSObject {
}
此代码的输出将是:
The type of the array elements = Foo
An instance of the array element = <EVReflection_iOS_Tests.Foo: 0x7fd6c20173d0>
An instance of the array element = Foo
Swift 5
它很旧,但如果有人需要,我想分享我的版本。
我使用 ModelProtocol
,我建议你使用协议,这样我们就可以通过协议进行操作来建模(例如:静态实例化)。
protocol ModelProtocol {}
class Foo: ModelProtocol {
}
因为我无法检查类型是否为数组,所以我使用 CollectionProtocol
并创建 Array
扩展以通过协议获取 Element
。
protocol CollectionProtocol {
static func getElement() -> Any.Type
}
extension Array: CollectionProtocol {
static func getElement() -> Any.Type {
return Element.self
}
}
测试中。
var fooArray: Array<Foo> = Array<Foo>()
var fooArrayAny: Any = fooArray
let arrayMirrorType = type(of: fooArrayAny)
String(describing: "arrayMirrorType: \(arrayMirrorType)")
if arrayMirrorType is CollectionProtocol.Type {
let collectionType = arrayMirrorType as! CollectionProtocol.Type
String(describing: "collectionType: \(collectionType)")
let elementType = collectionType.getElement()
String(describing: "elementType: \(elementType)")
let modelType = elementType as! ModelProtocol.Type
String(describing: "modelType: \(modelType)")
// ... now you can do operation to modelType via ModelProtocol
}
正在打印。
arrayMirrorType: Array<Foo>
collectionType: Array<Foo>
elementType: Foo
modelType: Foo