【问题标题】:Cocoa Scripting: Use special string types like raw dataCocoa Scripting:使用特殊的字符串类型,如原始数据
【发布时间】:2016-04-03 13:25:17
【问题描述】:

我的应用程序有一些我希望能够提供给 AppleScript 的原始数据内容,以便至少可以查看它,甚至可以通过将其保存到文件或将其设置为支持它的其他对象来处理它。

现在,我不明白使用哪种数据类型来实现这一点。

查看脚本编辑器的输出,例如:

tell application "Script Editor"
  the clipboard as record
    --> {Unicode text:"text",
         «class BBLM»:«data BBLM6C6C756E»,
         string:"text"}
end tell

我如何返回这些 «data ...»,它们显然是实际数据的 4 字符代码和十六进制字符串编码字节的组合。

我尝试从我的脚本属性返回一个包含原始字节数据的 NSData 对象,但这不起作用。

更新

看来这与实现scripting<type>Descriptorscripting<type>WithDescriptor 有关。除了在 Sketch 示例代码中使用之外,我找不到任何关于此的文档。我假设如果我碰巧在我的 Sdef 中定义了这样的自定义类型,则会为该类型调用这些。

但是:我不会提前知道我要发送的类型,所以我无法在 Sdef 中预先定义它们。我的情况更像the clipboard:我有我想返回的类似剪贴板的数据,所以我只知道它们在运行时的 4 字符类型。这意味着我不会被这些处理程序询问。必须有其他方法可以通用地创建和接收这些类型,就像剪贴板实现一样。

【问题讨论】:

  • NSData 对象用于自定义 value-types 符合 NSCoding 不能由 AppleScript 支持的基本类表示。例如,基本的 AppleScript 类型 pointrectangle 是包装在 NSData 中的 C 类型。要定义自定义 value-type,您必须在 sdef 文件中提供目标类,并在目标类的类别中实现 scripting<type>... 方法。
  • 在我的另一个问题 (stackoverflow.com/questions/36363705) 中根据您的 cmets 进行了更多挖掘之后,我终于弄清楚了您的意思,而我还没有理解,即一切都来了使用 NSAppleEventDescriptor,这是每个 Cocoa 类型最终在将值返回给正在运行的 Applescript 之前分配给的类型。
  • 确实,Cocoa 脚本可能是一件很棒的事情,但文档记录很差,而且很容易出错。在瑞士,我们会说“s'isch en saich”(类似于“pain in the ass”)
  • 所以,感谢您的帮助,没有您的指点,我还没有弄清楚这一点,尽管现在一切都是如此“当然,这完全有道理!” :) 我希望这在将来也能帮助其他人。对于那些在 Cocoa 层之前使用过 AS 的人来说,这可能都是显而易见的东西,但我完全被难住了。

标签: macos cocoa applescript cocoa-scripting


【解决方案1】:

RE:“...实现脚本Descriptor 和脚本WithDescriptor。我找不到关于此的任何文档...”

首先要从 Cocoa Scripting Guide (2008) 中的“Key-Value Coding and Cocoa Scripting”部分开始。有很多这些方法将类型嵌入方法名称中。许多也记录在基金会的 NSScriptKeyValueCoding 协议参考页面中,但您必须阅读“讨论”部分才能找到它们。例如,在:

- (id)valueWithUniqueID:(id)uniqueID inPropertyWithKey:(NSString *)key

讨论说:“方法 valueInWithUniqueID: 如果存在则调用。”

因此,在 Widgets 类中,您将实现 valueInWidgetsWithUniqueID:

scriptingDescriptor 和 scriptingWithDescriptor 是在应用程序的 .sdef 中使用元素时使用的特殊转换处理程序,这就是为什么它们出现在 Sketch 中以处理 typeRGBColor 数据类型,列表3 个整数。我也无法在 Sketch 代码之外找到这些文档,但我可以确认

scriptingRGBColorDescriptor

由以下方法调用:

NSObject(NSScriptAppleEventConversion)
NSAppleEventDescriptor(NSScriptConversion)

【讨论】:

  • 我的测试表明即使不实现scripting<Key>WithDescriptor函数,也会通过valueWithUniqueID:处理程序得到原始的NSAppleEventDescriptor,然后可以提取data和@987654328 @来自它。
  • 一旦在 Sdef 中为属性指定了特定类型,实现scripting<Key>Descriptor 就变得很有必要了。如果使用“any”作为类型,则不需要。
  • 这似乎证实了它们被称为描述符强制过程的一部分。当您指定“任何”类型时,您是在告诉引擎不要强制执行结果。
【解决方案2】:

RE:“但是:我不会提前知道我要发送的类型,所以我无法在 Sdef 中预先定义它们。”

有一种方法可以解决这个问题:您可以返回一个称为用户字段记录 (typeUserField) 的特殊列表结构。该记录包括交替的 Key 和 Value 描述符,不需要在 SDEF 中定义任何内容。

这是我去年在 ASOC 邮件列表上发布的一个项目: http://lists.apple.com/archives/applescriptobjc-dev/2015/Jan/msg00036.html

这是从 NSDictionary 构建 typeUserField 记录的代码(使用 AppleScript-ObjectiveC 代码)。

# ASOC implementation of - (NSAppleEventDescriptor *)scriptingRecordDescriptor for an NSDictionary
# Creates an empty record descriptor and an empty list descriptor, then
# Iterates over the dictionary and inserts descriptors for each key and each value into the list descriptor
# Finally, populates the record descriptor with the type 'usrf' and the list descriptor

on makeUserRecordDescriptor(aDict)

log aDict

set recordDescriptor to aedClass's recordDescriptor()
set listDescriptor   to aedClass's listDescriptor()

set typeUserField to 1970500198 -- 'usrf'

set itemIndex to 1 -- AS records are 1-based

repeat with aKey in aDict's allKeys()

    set aVal to aDict's valueForKey_(aKey)

    -- The values can be several different types. This code DOES NOT handle them all.

    set isStringValue  to aVal's isKindOfClass_(nssClass's |class|) = 1
    set isNumericValue to aVal's isKindOfClass_(nsnClass's |class|) = 1
    set isBooleanValue to aVal's className()'s containsString_("Boolean") = 1

    -- Insert a descriptor for the key into the list descriptor

    set anItem to aedClass's descriptorWithString_(aKey)
    listDescriptor's insertDescriptor_atIndex_(anItem, itemIndex)
    set itemIndex to itemIndex + 1

    -- Insert a descriptor (of the correct type for the value) into the list descriptor

    if isStringValue
        set anItem to aedClass's descriptorWithString_(aVal)
    else if isBooleanValue
        set anItem to aedClass's descriptorWithBoolean_(aVal's boolValue())
    else if isNumericValue
        set intValue to aVal's intValue()
        set fpValue to aVal's doubleValue()

        if intValue = fpValue
            set anItem to  aedClass's descriptorWithInt32_(aVal's intValue())
        else
            set anItem to  aedClass's descriptorWithString_(aVal's stringValue) # TODO: 'doub'
        end
    else
        set anItem to  aedClass's descriptorWithString_("Unhandled Data Type")
    end

    listDescriptor's insertDescriptor_atIndex_(anItem, itemIndex)
    set itemIndex to itemIndex + 1

end

recordDescriptor's setDescriptor_forKeyword_(listDescriptor, typeUserField)

return recordDescriptor

结束

【讨论】:

    【解决方案3】:

    神奇之处在于使用NSAppleEventDescriptor。它提供了很多初始化程序。它最终拥有任何值,这些值会被传递回调用 AppleScript(或 JXA 或任何使用脚本引擎的东西)。

    显然,任何返回到 Cocoa 脚本层的值,例如 NSString 的字符串和 NSNumber 的数值,最终都会分配给 NSAppleEventDescriptor 对象,并通过该步骤转换为 AppleEvent 内部格式。

    所以,如果我想返回一串字节,例如存储在 NSData 对象中,我所要做的就是通过我的属性方法:

    -(id)returnSomeBytes {
        return [NSAppleEventDescriptor descriptorWithDescriptorType:'Raw ', myNSDataObject];
    }
    

    这将在 AppleScript 中以 «data Raw ...» 结尾。

    我现在也明白为什么脚本引擎不会为我自动转换 NSData:它需要一个类型代码,而 NSData 不会继承它。

    反之亦然 - 任何此类原始数据都会作为 NSAppleEventDescriptor 传递给我的代码,然后我可以对其进行相应解码。

    【讨论】:

      猜你喜欢
      • 2022-11-25
      • 2015-09-06
      • 2017-04-25
      • 2016-10-24
      • 2019-08-17
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多