【问题标题】:ios load objective c class vs swift class in reflection using objective cios使用objective c在反射中加载objective c类与swift类
【发布时间】:2021-11-26 13:11:01
【问题描述】:

我正在尝试在目标 c 中使用反射加载一个类,并为 swift 类和目标 c 类获得不同的结果

我看到的是,使用 NSClassFromString,对于一个目标 c 类,我只需要提供 类名 作为输入,而对于一个 swift 类,我需要输入 bundle id 作为前缀

我想知道为什么会这样,以及是否有办法在使用相同输入的两个平台上使用一个代码

以下示例

例如

目标c类 - A

swift 类 - B

项目的bundle id是Test

Class  test1 = NSClassFromString(@"A"); // works

Class  test2 = NSClassFromString(@"Test.A"); // doesn't work

Class  test3 = NSClassFromString(@"B"); // doesn't work

Class  test4 = NSClassFromString(@"Test.B"); // works

我尝试在 swift 中做同样的事情并遇到同样的问题

var  test1 = NSClassFromString("A") as? A.Type // works

var  test2 = NSClassFromString("Test.A") as? A.Type // doesn't work

var  test3 = NSClassFromString("B") as? B.Type // doesn't work

var  test4 = NSClassFromString("Test.B") as? B.Type // works

男人

【问题讨论】:

  • 那么使用A 用于ObjC 类和Test.B 用于swift 有什么问题?
  • 我想知道是否有 API 可以让同一个字符串同时适用于目标 c 和 swift 类
  • 你已经找到了一个 API,问题是 swift 是按模块组织的,这是 objc 中不存在的概念。但是通过命名约定,可以找到并区分类名是 objc 还是 swift .. 例如前面带有 . 和模块名称。不鼓励在其他语言空间中寻找类名,但显然是可能的。因为两者都有其特定的桥接机制到您遇到麻烦的另一种语言。意味着您可以使用@objc(desiredname) 显式公开一个 swift 类,并在正确完成桥接后在具有该名称的 objc 中找到它。

标签: ios swift objective-c reflection


【解决方案1】:

Swift 类使用命名空间并在模块中排序

所以你可以扩展 NSObject 并使用辅助方法

// NSObject+ExposedSwiftClassExtension.h
// make use of @objc exposed swift classes
// with mainBundle name
//
#import <Foundation/Foundation.h>

@interface NSObject (ExposedSwiftClassExtension)

/** @name classWithNameAndTryDifferentIfFail:
    gets Class with NSString, if fails try to find in MainBundle
    or with last component, otherwise nil.
    @discussion should find Class with Name in first attempt, if 
    no success tests for "." in objcString and tries without
    preleading module name or within MainBundle as last resort.
    If all three ways fail returns nil.
    @param objcString a NSString as assumed ClassName
    @return Class or nil if not found.
 */
+ (Class _Nullable)classWithNameAndTryDifferentIfFail:(NSString* _Nonnull)objcString;

@end
// NSObject+ExposedSwiftClassExtension.m
// ExposedSwiftClassExtension,
// make use of @objc exposed swift classes
// but they use namespace and module name
// so this extension makes it convenient
// if module name is bundle name
//
#import "NSObject+ExposedSwiftClassExtension.h"

@implementation NSObject (ExposedSwiftClassExtension)
+(Class)classWithNameAndTryDifferentIfFail:(NSString*)objcString {
    Class desiredClass = NSClassFromString(objcString);
    if (desiredClass!=nil) return desiredClass;
    NSString *classname = nil;
    if ([objcString containsString:@"."]) {
        // contains "." means you assume module but try without here.
        classname = [objcString componentsSeparatedByString:@"."].lastObject;
    } else {
        // last resort, try with BundleName
        NSString *bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
        if (bundleName) {
            classname = [bundleName stringByAppendingFormat:@".%@",objcString];
        }
    }
    // if classname is still nil here should return nil anyway.
    return classname ? NSClassFromString(classname) : nil;
}
@end

然后使用Class test2 = [NSObject classWithNameAndTryDifferentIfFail:@"Test.A"];

PS:对于 objc 初学者.. 正如您在代码中看到的,ClassName 冲突很容易完成,当 Classnames 是唯一的并且 “ExposedSwiftClassExtension”可以更改为任何不同的名称,如果我为什么选择该名称令人困惑。它的意思是提醒您无法从 Objective-C 中找到未公开的 swift 类。扩展本可以用 Swift 编写,但会使在 ObjC 中的使用更加困难。

【讨论】:

  • 谢谢,通过阅读,听起来苹果以不同的方式实现了目标 c 和 swift 类,因此 swift 是从不同的字符串创建的。目标 C.那是我的问题
  • 是的。 ObjC 使用的字符串使其非常灵活,这就是NSClassFromString 存在且有用的原因。 Swift 在这个意义上是不同的,因此函数名称需要显式地暴露给 ObjC,但提供类似的函数来完成类似的工作。通过命名约定桥接 ObjC 2 Swift 是一个半自动化的过程,桥接 Swift 2 ObjC 是通过在前面使用 @objc 标记函数和/或类来显式完成的。标记 swift @objc 并在 objc 中使用单个声明更容易,该声明可以桥接到 swift 以在两者中使用它,然后反过来或声明两次..
猜你喜欢
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
  • 2021-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
相关资源
最近更新 更多