在为我的 Mac 程序添加脚本功能时,我正在努力解决 的常见编程问题。从索引数组中删除项目 由于删除项目,项目索引发生变化。
假设我的应用程序维护一个数据存储,其中存储了“Person”类型的对象。在 sdef 中,我定义了 Cocoa Key allPersons
访问这些元素。我的应用声明了 NSArray *allPersons
.
到目前为止,它运作良好。例如,这个脚本运行良好:
repeat with p in every person
get name of p
end repeat
当我想支持删除项目时,问题就开始了,如下所示:
repeat with p in (get every person)
delete p
end repeat
(我意识到我可以只写“删除每个人”,这很好,但我想展示“重复”如何使事情变得更复杂)。
这不起作用,因为即使在删除其中一些项目之后,AppleScript 仍继续使用原始项目编号来引用这些项目,这自然会改变项目及其编号。
因此,考虑到我们有 3 个人,“Adam”、“Bonny”和“Clyde”,这将发生:
get every person
--> {person 1, person 2, person 3}
delete person 1
delete person 2
delete person 3
--> error number -1719 from person 3
删除第 1 项(亚当)后,其他项目重新编号为第 1 项和第 2 项。第二次迭代删除第 2 项(现在是克莱德),第三次迭代尝试删除第 3 项,该第 3 项不再存在那一点。
我该如何解决这个问题?
我可以强制脚本引擎不使用它们的索引号而是使用它们的唯一 ID 来处理这些项目,这样就不会发生这种情况吗?
最佳答案
不是你的 ObjC 代码,是你对 repeat with VAR in EXPR
的误解循环工作。 (也不是你的错:它们 1. 违反直觉,2. 解释不清。)当它第一次遇到你的 repeat
时声明,AppleScript 会向您的应用发送 count
事件以获取 EXPR
指定的项目数,在这种情况下是一个对象说明符(查询),它标识所有 person
任何元素。然后它使用该信息生成自己的按索引对象说明符序列,从 1 计数到上述 count
的结果。 :
person 1 of whatever
person 2 of whatever
...
person N of whatever
您需要意识到的是,对象说明符是一等查询,而不是对象指针(Apple 也没有告诉您):它描述的是请求,而不是对象。忽略被盗用的行话:Apple 事件 IPC 的近亲是 RDBMS,而不是 Cocoa 或 SOAP 或任何现代开发人员如此关注的 OO 消息传递工具 一种真正的方法... 好吧,一切 .
只有当该查询在 Apple 事件中发送到您的应用程序时,它才会根据您的 Apple 事件 IPC View Controller (也称为“Apple 事件对象模型”)的关系图进行评估 – 呈现为模型用户的理想化、用户友好表示它实际解析为特定模型对象或对象的日期,事件处理程序应使用该对象执行请求的操作。
因此,当
delete
您的 repeat
中的命令循环告诉你的应用程序到 delete person 1 of whatever
,所有剩余的元素都向下移动一个。但是在下一次迭代中 repeat
循环仍然生成对象说明符 person 2 of whatever
,然后您的脚本将其发送到您的应用程序,该应用程序将其解析为集合中的第二个项目 - 当然,这最初是第三个项目,直到您将它们全部转移。或者,借用一句话:
除了关系查询之外,AppleScript 中没有任何意义。
..
事实上,Apple 事件的基于查询的方法实际上很有意义,因为它最初被设计为在非常高延迟的连接上高效(即 System 7 的效率极低的进程切换器),允许单个 Apple 事件携带一个或多个一次操作多个对象的复杂查询。它甚至非常优雅[当它工作正常时],但被库比蒂诺的白痴完全取消了,他们认为让程序员不讨厌这项技术的最好方法是更加努力地说明它是如何工作的。
所以在这里,我建议你go read this ,这也不是最好的解释,但仍然比你从那些布偶那里得到的任何东西都要好。和this , 由它的原始设计者解释了创建高级粗粒度基于查询的 IPC 系统而不是通常的低级细粒度 OO 消息传递废话的许多基本原理。
哦,一旦你这样做了,你可能要考虑尝试运行它:
delete every person whose name is "bob"
这几乎是创建一个厚的声明性抽象的全部意义,它可以完成所有工作,因此用户不必这样做。
当除了命令式客户端循环之外什么都不会做时,您要么想先从应用程序获取按 ID 对象说明符(这是最接近 AEOM 可以做的安全、持久指针的事情)列表,然后再迭代那,或者至少使用您自己的迭代器循环来反向计算元素:
repeat with i from (count every person) to 1 by -1
tell person i
..
end tell
end repeat
因此,假设它在服务器端迭代一个有序数组,将从最后一个到第一个删除,从而避免原始脚本的令人尴尬的 N 个错误。
高温高压
关于objective-c - cocoa 脚本 : Deletion of elements in a loop getting out of sync,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36571009/