使用 Cocoa 应用程序中嵌入的 AppleScript-ObjC 处理错误的最佳方法?

Posted

技术标签:

【中文标题】使用 Cocoa 应用程序中嵌入的 AppleScript-ObjC 处理错误的最佳方法?【英文标题】:Best Way to Handle Errors with AppleScript-ObjC embedded in Cocoa App? 【发布时间】:2013-08-02 17:33:26 【问题描述】:

我正在从一个较大的 Cocoa/Objective-C 应用程序中调用一个小的 AppleScript 处理程序(使用 AppleScript-ObjC 框架直接从 Objective-C 代码调用 AppleScript 方法)。我最初尝试使用 Scripting Bridge,但由于与外部应用程序的兼容性问题,这对我不起作用。 AppleScript 的唯一目的本质上是向外部应用程序发送一个小字符串。这是我第一次尝试做这样的事情来控制外部应用程序,所以如果我犯了明显的错误,请多多包涵。

有时,AppleScript 可能会根据外部应用程序的状态遇到错误,我想在我的 Objective-C 代码中适当地处理这些错误。

我现在这样做的方式是我在 AppleScript 中有一个 try 块:

try
    -- Do stuff
    return the number 0
on error the error_message number the error_number
    return the error_number
end try

如果脚本正常完成,则返回零,否则返回错误号。然后在 Objective-C 代码中,我测试返回值并在出现错误的情况下抛出一个“正常”异常,以便我可以在我的常规异常处理代码中处理它。

这可行,但是有没有办法直接从 Objective-C 代码中捕获 AppleScript 异常?当我在 XCode 中运行没有 AppleScript try-error 代码的代码时,它似乎在从 AppleScript 返回之前命中了我的异常断点,但它从未为 NSException 调用 @catch 块。有什么方法可以捕获以某种方式被击中的异常吗?我猜答案是否定的,但我想检查没有比我正在做的更好的方法来做到这一点。 Apple 关于 AppleScript-ObjC 的文档非常少,而且大多只是讨论如何处理 AppleScript 中的错误。

更新: 所以,我只是尝试了一点断点设置,结果发现这是从 AppleScript 代码发送的 C++ 异常,而不是 Objective-C 异常。这就是为什么它没有被我使用的@catch 块捕获。似乎 AppleScript-ObjC 机制对控制流/可恢复错误使用异常,并且我将断点设置为在抛出异常时中断。所以,我认为最好只捕获 AppleScript 应用程序中的错误,并将错误代码作为字符串或整数返回到 Objective-C 代码。

【问题讨论】:

我无法具体回答您的问题,但我会指出,在 Objective-C/Cocoa 中,异常意味着真正的异常情况,通常是由于程序员错误而不是可能导致的错误可以安全地从中恢复。来自documentation:“Cocoa 框架通常不是异常安全的。一般模式是异常只为程序员错误保留,捕获此类异常的程序应该很快退出。” 这是一个很好的观点,尽管可能来自 AppleScript 的一些错误很可能是不可恢复的错误。我问这个问题的唯一原因是,调用 AppleScript 代码似乎会引发一个异常,即使对于轻微的、可恢复的错误,它也会触发我的异常断点……但据我所知,这不是一个正常的 Cocoa NSException。所以我只想知道我是否能以某种方式从我的 Objective-C 代码(而不是 AppleScript)中捕捉到这一点。 有趣。你是如何设置异常断点的?断点触发时的堆栈跟踪是什么样的? 你是如何在代码中运行 AppleScript 的。并发送字符串。我问这个是因为 NSApplescript 有 executeAndReturnError: 方法。 我正在使用 AppleScript-ObjC (ASOC) 框架来运行它。 (Apple 有大约 4 种从 Objective-C 代码运行 AppleScript 事件的方法)。使用 ASOC,您可以将 AppleScript 代码直接嵌入到您的 Objective-C 程序中,并从 Objective-C 调用在 AppleScript 中实现的方法(尽管设置它的过程还有一些不足之处:您必须实现一个 NSObject 类别来告诉 Objective-C AppleScript 方法的签名,因为它们没有头文件)。我使用这种方法是因为它似乎比使用带有字符串的 NSAppleScript 更好。 【参考方案1】:

AppleScript 使用 C++ 异常来实现自己的异常,但仅此而已;你不应该在成功完成的代码中看到它们。如果 AppleScript 异常逃逸到返回到 Objective-C 的点,桥实际上会吞下它——您应该得到一条日志消息,以及来自桥接方法的 nil/零/NO 返回值。所有这一切的结果是您已经在做正确的事情:在 AppleScript 中捕获错误并返回您的 Objective-C 代码可以检测到的内容。

附带说明,AppleScriptObjC 主要面向主要或完全使用 AppleScript 编写应用程序的开发人员。如果您正在编写一个主要的 Objective-C 应用程序,其中包含一些通过脚本控制其他应用程序的位,请考虑改用 ScriptingBridge。

【讨论】:

除了 ScriptingBridge 不能正常工作并且经常中断在 AS 中完美工作的东西。说 Apple 事件正确性的唯一受支持的方式是 AS,因此能够使用 ASOC 而不是弄乱 NSAppleScript 废话对 ObjC 用户来说也是一个很大的好处。 FWIW,我一直在试图弄清楚 ASOC 的 ObjC->AS 参数映射是如何工作的,但运气不佳。目前,我认为最不糟糕的选择可能是返回一个包含 2 个值的 NSArray:结果(返回值或“缺失值”)和错误信息(“缺失值”或 NSError);但如果我找到更好的方法,我会发布它。 是的。正如我上面所说,我最初尝试使用 Scripting Bridge 并发现它对于某些应用程序来说是一场噩梦。我喜欢 Scripting Bridge 的想法,但它在实践中无法正常工作,因为(好的)想法实施得非常糟糕。只是为了让它自动生成可以编译的标头,需要手动编辑字典文件,并且一些 AppleScript 事件根本不起作用(而其他事件则起作用),因为与正常工作的正常 AS 代码完全相同的操作。 ASOC 工作得更好,尽管它也不是没有缺点。 是的,Apple 真的搞砸了 SB。 ObjC-Appscript 工作(有效?)绝对是一种享受,很遗憾 Apple 没有从中学习/复制/窃取它,但由于 Apple 遗留/弃用了它所依赖的 Carbon API,它不再得到维护或支持提供任何可可替代品。对于现在从 ObjC 做任何不平凡的事情,AppleScript-ObjC 通常是最好的选择:AppleScript 可能是一种 PITA 作为一种语言,但它知道如何正确地说出 Apple 事件,并且 ASOC 桥接器消除了连接二。 1) 如果 ScriptingBridge 在某些应用程序上无法正常工作,请提交错误。如果他们不了解它们,他们将不会修复它们。 2) ObjC-Appscript 并不像作者想象的那样注定要失败。 Apple Event Manager 文档移至“已停用”库,但所有调用仍标记为可用且未弃用。相信标题。 1) 我愿意向 Apple 提交错误并且偶尔会这样做,尽管我发现我在雷达上提交的大多数错误都会被完全忽略(甚至没有标记为重复),即使我给出详细的测试用例和示例代码。所以我开始认为这个练习有点毫无意义。 2) SB 确实存在一些严重的错误/问题,因为它对于与普通 AppleScript 一起正常工作的应用程序会失败。 3) 很高兴听到这个消息,尽管我更喜欢 Apple 解决方案(如果存在的话)(考虑到他们提出的过多的半竞争性想法,Apple 的可脚本性似乎是一个问题。)

以上是关于使用 Cocoa 应用程序中嵌入的 AppleScript-ObjC 处理错误的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

带有嵌入库的 Cocoa Touch Framework Swift

Cocoa Touch 框架无法在嵌入项目中的模拟器上调试

Cocoa:如何在 Mail.app 中绘制插入文本?

Cocoa Touch 框架无法识别

如何在 Cocoa 中创建这种面板?

如何解决 Cocoa touch 框架中的符号名称冲突