【问题标题】:How do I let Scripting Bridge generate Objective-C classes at runtime?如何让 Scripting Bridge 在运行时生成 Objective-C 类?
【发布时间】:2023-09-08 18:53:02
【问题描述】:

我今天正在更新 Scripting Bridge 胶水文件(用于 Mail.app),并注意到 sdp(1) man page 显示为:

不需要创建相应的实现文件; Scripting Bridge 将创建 运行时的类实现。

这听起来很酷,但我得到的生成类没有实现(如预期的那样):

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_MailApplication", referenced from:
      objc-class-ref in DMBugReportWindowController.o
  "_OBJC_CLASS_$_MailAttachment", referenced from:
      objc-class-ref in DMBugReportWindowController.o
[... more of the same ...]
ld: symbol(s) not found for architecture x86_64`

我不想抑制所有个未定义的符号,因为这很容易掩盖合法问题,所以我只使用了-U(根据ld(1) man page):

指定符号名可以没有定义。和 -two_levelnamespace,生成的符号将被标记为 dynamic_lookup 这意味着 dyld 将搜索所有加载的图像。

(我必须使用 -Xlinker -U -Xlinker _OBJC_CLASS_$_MailApplication 让这些标志到达 ld,否则 clang 会保留这些参数。)

显然将它们设为dynamic_lookup 是错误的做法,因为这会在启动时产生动态链接错误:

dyld: Symbol not found: _OBJC_CLASS_$_MailApplication
  Referenced from: /Users/jonathon/Library/Developer/Xcode/...
  Expected in: flat namespace
 in /Users/jonathon/Library/Developer/Xcode/...

如果我使用-force_flat_namespace -undefined suppress(我不想使用,如上所述),也会发生这种情况。 Scripting Bridge Programming Guide 似乎暗示我做事正确(“准备代码”部分),但没有提到这个问题。

【问题讨论】:

    标签: objective-c cocoa linker ld scripting-bridge


    【解决方案1】:

    由于该类是动态创建的,因此您无法链接到它。该类将在运行时注册,但不会导出任何符号,并且链接器将无法找到它。

    您需要将发送至MailApplicationMailAttachment 的所有消息替换为发送至NSClassFromString(@"MailApplication")NSClassFromString(@"MailAttachment") 的消息。

    [MailApplication class] 变为 NSClassFromString(@"MailApplication")[MailAttachment class] 变为 NSClassFromString(@"MailAttachment")

    【讨论】:

    • 这可以解决这个问题,但问题中链接的脚本桥文档说,“您可以使用 id 动态键入脚本桥对象,[但是你没有得到类型检查]。建议您生成一个头文件 [...]。这样做可以让编译器检查对象的类型(消除虚假的警告消息),并让您更加确信您正在向正确的收件人发送消息。 " 这对我来说意味着有一种方法可以做到这一点,而无需将所有内容都输入为 'id'。否则文档可能会很烂。
    • 不,您仍然可以使用 MailApplicationMailAttachment 作为对象的类型。但是您不能在消息表达式中使用它们。我编辑了答案。
    • 啊,好先生。您必须调用 [SBApplication applicationWithBundleIdentifier:] 或类似方法,而不是使用 [MailApplication application],因为 MailApplication 类在您调用之前不存在于运行时中。