如何在 Swift 中成功编译 NSClassFromString
How to successfully compile NSClassFromString in Swift
以下代码无法编译successfully.I还附上截图供参考!
func getXibViewWithClassNameString(classNameString:String)->AnyObject?{
let projectName = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String
let MyClass:AnyClass = NSClassFromString(projectName! + "." + classNameString)!
var viewArray:NSArray?
var xibView:AnyClass?
Bundle.main.loadNibNamed(classNameString, owner: nil, topLevelObjects: &viewArray)
for viewInArray in viewArray ?? [] {
if (viewInArray is MyClass){
xibView = viewInArray as MyClass
}
}
return xibView
}
screenshot for reference
您可以使用
进行测试
if viewInArray.isKind(of: MyClass) { ... }
或
if viewInArray.isMember(of: MyClass) { ... } // strict class match
但我认为您不能将编译时不可用的 class 强制转换为 Swift。无论如何,您将无法直接调用它的任何方法。不过,您可以将其转换为某些 NSObject
子 class 并向其发送任意(未在编译时定义)消息。
这里的主要错误是您无法 as
或 is
检查动态类型。您需要有一个在编译时已知的静态类型才能使用它。正如 pointum 所指出的,您需要在基础 NSObject 上使用 isKind(of:)
或 isMember(of:)
。以下是我建议的做法:
func firstXibViewOfClass(named classNameString: String) -> NSView? {
// Create an AnyClass object
guard let projectName = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String,
let myClass = NSClassFromString(projectName + "." + classNameString)
else {
return nil
}
// Load the nib
var topLevelObjects: NSArray?
Bundle.main.loadNibNamed(classNameString, owner: nil, topLevelObjects: &topLevelObjects)
// Convert it to NSObjects (since they all are going to be)
guard let nsObjectArray = topLevelObjects as? [NSObject] else { return nil }
// Find the first matching view and return it as an NSView if possible
return nsObjectArray.first(where: {
[=10=].isKind(of: myClass) // Or isMember(of:) if you want to be strict
}) as? NSView
}
不过,如果您不需要包含子类,您可能只想直接检查类名。这摆脱了所有 AnyClass 东西:
func firstXibViewOfClass(named classNameString: String) -> NSView? {
// Load the nib
var topLevelObjects: NSArray?
Bundle.main.loadNibNamed(classNameString, owner: nil, topLevelObjects: &topLevelObjects)
// Convert it to NSObjects (since they all are going to be)
guard let nsObjectArray = topLevelObjects as? [NSObject] else { return nil }
// Find the first matching view and return it as an NSView if possible
return nsObjectArray.first(where: { [=11=].className == classNameString }) as? NSView
}
您当然可以 return AnyObject?
,但该方法的名称表明您希望它是一个视图,因此您应该强制执行或重命名该方法。
以下代码无法编译successfully.I还附上截图供参考!
func getXibViewWithClassNameString(classNameString:String)->AnyObject?{
let projectName = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String
let MyClass:AnyClass = NSClassFromString(projectName! + "." + classNameString)!
var viewArray:NSArray?
var xibView:AnyClass?
Bundle.main.loadNibNamed(classNameString, owner: nil, topLevelObjects: &viewArray)
for viewInArray in viewArray ?? [] {
if (viewInArray is MyClass){
xibView = viewInArray as MyClass
}
}
return xibView
}
screenshot for reference
您可以使用
进行测试if viewInArray.isKind(of: MyClass) { ... }
或
if viewInArray.isMember(of: MyClass) { ... } // strict class match
但我认为您不能将编译时不可用的 class 强制转换为 Swift。无论如何,您将无法直接调用它的任何方法。不过,您可以将其转换为某些 NSObject
子 class 并向其发送任意(未在编译时定义)消息。
这里的主要错误是您无法 as
或 is
检查动态类型。您需要有一个在编译时已知的静态类型才能使用它。正如 pointum 所指出的,您需要在基础 NSObject 上使用 isKind(of:)
或 isMember(of:)
。以下是我建议的做法:
func firstXibViewOfClass(named classNameString: String) -> NSView? {
// Create an AnyClass object
guard let projectName = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String,
let myClass = NSClassFromString(projectName + "." + classNameString)
else {
return nil
}
// Load the nib
var topLevelObjects: NSArray?
Bundle.main.loadNibNamed(classNameString, owner: nil, topLevelObjects: &topLevelObjects)
// Convert it to NSObjects (since they all are going to be)
guard let nsObjectArray = topLevelObjects as? [NSObject] else { return nil }
// Find the first matching view and return it as an NSView if possible
return nsObjectArray.first(where: {
[=10=].isKind(of: myClass) // Or isMember(of:) if you want to be strict
}) as? NSView
}
不过,如果您不需要包含子类,您可能只想直接检查类名。这摆脱了所有 AnyClass 东西:
func firstXibViewOfClass(named classNameString: String) -> NSView? {
// Load the nib
var topLevelObjects: NSArray?
Bundle.main.loadNibNamed(classNameString, owner: nil, topLevelObjects: &topLevelObjects)
// Convert it to NSObjects (since they all are going to be)
guard let nsObjectArray = topLevelObjects as? [NSObject] else { return nil }
// Find the first matching view and return it as an NSView if possible
return nsObjectArray.first(where: { [=11=].className == classNameString }) as? NSView
}
您当然可以 return AnyObject?
,但该方法的名称表明您希望它是一个视图,因此您应该强制执行或重命名该方法。