JXA:从CoreServices访问CFString常量

时间:2015-10-24 04:41:17

标签: objective-c javascript-automation core-services jxa

JXA及其内置的ObjC桥接器通过Foundation对象自动公开$框架中的枚举和常量; e.g:

$.NSUTF8StringEncoding  // -> 4

但是,在自动导入的低级API中也有一些有用的CFString常量,即kUTType*中用于定义常用UTI的常量CoreServices常量}值,例如kUTTypeHTML用于UTI "public.html"

虽然您可以使用ObjC.import('CoreServices') 导入,但字符串值不能(轻松)访问,大概是因为其类型为CFString[Ref]

ObjC.import('CoreServices') // import kUTType* constants; ObjC.import('Cocoa') works too
$.kUTTypeHTML  // returns an [object Ref] instance - how do you get its string value?

我还没有找到一种方法来获取返回内容的字符串ObjC.unwrap($.kUTTypeHTML)不起作用,ObjC.unwrap($.kUTTypeHTML[0])(也不.deepUnwrap())也不起作用。

我想知道:

  • 如果有一种原生的JXA方法可以做到这一点,我就错过了。
  • 否则,如果没有使用ObjC.bindFunction()来定义可以解决问题的CFString*()函数的绑定,例如CFStringGetCString()CFStringGetCStringPtr(),但这并不明显告诉我如何翻译ObjC签名。

3 个答案:

答案 0 :(得分:2)

虽然我不明白所有含义,但以下似乎有效:

$.CFStringGetCStringPtr($.kUTTypeHTML, 0) // -> 'public.html'

# Alternative, with explicit UTF-8 encoding specification
$.CFStringGetCStringPtr($.kUTTypeHTML, $.kCFStringEncodingUTF8) // ditto

kUTType*常量定义为CFStringRefCFStringGetCStringPtr以指定的编码返回CFString对象的内部C字符串, if 可以提取"with no memory allocations and no copying, in constant time" - 或NULL

使用内置常量,似乎总是返回一个C字符串(而不是NULL),它通过映射到JXA数据类型的C数据类型直接在JavaScript中使用:< / p>

 $.CFStringGetCStringPtr($.kUTTypeHTML, 0) === 'public.html' // true

对于背景信息(从OSX 10.11.1开始),请继续阅读。

JXA本身并不识别CFString个对象,即使它们可以“免费桥接”到NSString,这是JXA 识别的类型。

您可以通过执行CFString验证JXA是否知道NSString$.NSString.stringWithString($.kUTTypeHTML).js的等效性,应该返回输入字符串的副本,但却以-[__NSDictionaryM length]: unrecognized selector sent to instance失败。

不承认CFString是我们的出发点:$.kUTTypeHTML的类型为CFString[Ref],但JXA不返回 JS 字符串表示形式,仅[object Ref]

注意:以下内容部分是推测性的 - 请告诉我,如果我错了。

不识别CFString会产生另一个副作用,即调用接受泛型类型的CF*()函数时(或接受免费桥接的{Cocoa方法} {{1 JXA不知道的类型):
在这种情况下,如果参数类型与调用函数的参数类型不完全匹配,则JXA显然隐式包装 CF*实例中的输入对象,其唯一条目具有键CFDictionary,其关联值包含原始对象。 [1]

据推测,这就是上述type调用失败的原因:它正在传递$.NSString.stringWithString()包装而不是CFDictionary实例。

另一个例子是CFString函数,它需要一个CFGetTypeID()参数:即任何 CFTypeRef类型。

由于JXA不知道将CF*参数作为CFStringRef参数原样传递是可以的,它会错误地执行上述包装,并有效地传递{{1}而实例代替:

CFTypeRef

这是houthakker {/ 3}}中遇到的his solution attempt。{/}

对于给定的CFDictionary功能,您可以绕过默认行为,方法是使用$.CFGetTypeID($.kUTTypeHTML) // -> !! 18 (CFDictionary), NOT 7 (CFString) 重新定义感兴趣的功能:

CF*

现在,ObjC.bindFunction()正确返回// Redefine CFGetTypeID() to accept any type as-is: ObjC.bindFunction('CFGetTypeID', ['unsigned long', [ 'void *']]) $.CFGetTypeID($.kUTTypeHTML))。

注意:重新定义的7返回JS CFString实例,而原始实例返回基础数字的{em>字符串表示($.CFGetTypeID()值)。

通常情况下,如果您想非正式地了解给定Number个实例的具体类型,请使用CFTypeID ,例如:

CF*

注意:CFShow()不返回任何内容,而是直接打印到 stderr ,因此您无法捕获JS中的输出。
您可以使用$.CFShow($.kUTTypeHTML) // -> '{\n type = "{__CFString=}";\n}' 重新定义CFShow(),以便不显示包装词典。

对于本机识别的CF *类型 - 映射到JS基元的类型 - 您将直接看到特定类型(例如,CFShow用于ObjC.bindFunction('CFShow', ['void', [ 'void *' ]]));对于未知 - 因此包装 - 实例,您将看到如上所述的包装结构 - 继续阅读。

[1] 在传递未知类型时,运行以下内容为您提供 由JXA生成的包装器对象 :

CFBoolean

同样,使用false// Note: CFShow() prints a description of the type of its argument // directly to stderr. $.CFShow($.kUTTypeHTML) // -> '{\n type = "{__CFString=}";\n}' // Alternative that *returns* the description as a JS string: $.CFStringGetCStringPtr($.CFCopyDescription($.kUTTypeHTML), 0) // -> (see above) 的已知与JXA等效,

NSDictionary

返回CFDictionary,即具有属性ObjC.deepUnwrap($.NSDictionary.dictionaryWithDictionary( $.kUTTypeHTML )) 的JS对象,其值为此时 - 在ObjC-bridge调用往返之后 - 仅仅字符串表示可能是原始{"type":"{__CFString=}"}实例的内容。

houthakker's solution attempt还包含一个方便的代码片段,用于获取type实例的 name 类型字符串。

如果我们将它重构为一个函数并应用CFString的必要重新定义,我们得到以下内容,但是:

  • 需要 hack 才能使其以可预测的方式返回值(请参阅注释和源代码)
  • 即便如此,随机字符有时也会显示为返回字符串的结尾,例如CF*而不是CFGetTypeID()

如果有人解释为什么需要黑客攻击以及随机角色来自哪里,请告诉我。这些问题可能与内存管理有关,因为CFString,CFString都会返回调用者必须释放的对象,而我不知道是否/如何/何时JXA那样做。

CFCopyTypeIDDescription()

答案 1 :(得分:2)

您可以通过首先重新绑定CFMakeCollectable函数将CF类型强制转换为NS类型,以便它可以使用&#39; void *&#39;并返回&#39; id&#39;,然后使用该函数执行强制:

ObjC.bindFunction('CFMakeCollectable', [ 'id', [ 'void *' ] ]);

var cfString = $.CFStringCreateWithCString(0, "foo", 0); // => [object Ref]
var nsString = $.CFMakeCollectable(cfString);            // => $("foo")

为了在代码中更容易使用,您可以在Ref原型上定义.toNS()函数:

Ref.prototype.toNS = function () { return $.CFMakeCollectable(this); }

以下是如何将此新函数与CFString常量一起使用:

ObjC.import('CoreServices')

$.kUTTypeHTML.toNS() // => $("public.html")

答案 2 :(得分:1)

$ .kUTTypeHTML似乎返回一个CFDictionary(见下文),因此您应该在以下位置找到可用的方法:

编辑:事实证明,JXA-ObjC-CF交互中的一些打字复杂性意味着下面的片段不是学习CF对象参考类型的可靠或普遍适用的方法。 (参见下面的讨论)。

https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFDictionaryRef/

ObjC.import('CoreServices')

var data = $.CFStringCreateExternalRepresentation(
        null, 
        $.CFCopyTypeIDDescription(
            $.CFGetTypeID($.kUTTypeHTML)
        ), 
        'UTF-8',
        0
    ); // CFDataRef


cPtr = $.CFDataGetBytePtr(data);

// --> "CFDictionary"