虽然我不理解所有的涵义,但以下内容似乎可以运作:
$.CFStringGetCStringPtr($.kUTTypeHTML, 0) // -> 'public.html'
# Alternative, with explicit UTF-8 encoding specification
$.CFStringGetCStringPtr($.kUTTypeHTML, $.kCFStringEncodingUTF8) // ditto
"
kUTType*
常量被定义为CFStringRef
,CFStringGetCStringPtr
以指定编码返回CFString
对象的内部C字符串,如果它可以在恒定时间内提取\"无需内存分配和复制\" - 否则返回NULL
。
使用内置常量时,似乎总是返回一个C字符串(而不是NULL
),由于C数据类型映射到JXA数据类型,因此可直接在JavaScript中使用:
"
$.CFStringGetCStringPtr($.kUTTypeHTML, 0) === 'public.html' // true
如需了解背景信息(截至OSX 10.11.1),请继续阅读。
JXA原生不识别CFString对象,尽管它们可以“无损桥接”到NSString类型,而这种类型JXA是认识的。您可以通过执行$.NSString.stringWithString($.kUTTypeHTML).js来验证JXA不知道CFString和NSString的等价性,该命令应返回输入字符串的副本,但实际上会失败并显示“-[__NSDictionaryM length]: unrecognized selector sent to instance”。不识别CFString是我们的起点:$.kUTTypeHTML的类型是CFString[Ref],但是JXA不会返回它的JS字符串表示,只有[object Ref]。注:以下部分内容是推测的,如果我说错了,请告诉我。
不认识CFString还会有另一个副作用,即在调用接受通用类型(或JXA不知道的过桥CF*类型的Cocoa方法)的CF*()函数时:
在这种情况下,如果参数类型与所调用的函数的参数类型不完全匹配,则JXA显然会隐式地将输入对象包装在一个CFDictionary实例中,该实例的唯一条目具有键“type”,相关值包含原始对象。
[1]
据推测,这就是为什么上面的$.NSString.stringWithString()调用失败的原因:它被传递了CFDictionary包装器而不是CFString实例。
另一个例子是CFGetTypeID()函数,它期望一个CFTypeRef参数:即任何CF*类型。
由于JXA不知道直接将CFStringRef
参数作为CFTypeRef
参数传递是可以的,它会错误地执行上述包装操作,实际上传递了一个CFDictionary
实例:
$.CFGetTypeID($.kUTTypeHTML) // -> !! 18 (CFDictionary), NOT 7 (CFString)
这是
houthakker在
解决问题的尝试中的经历。
对于给定的
CF*
函数,您可以使用
ObjC.bindFunction()
绕过默认行为,重新定义感兴趣的函数。
ObjC.bindFunction('CFGetTypeID', ['unsigned long', [ 'void *']])
现在,
$.CFGetTypeID($.kUTTypeHTML)
正确地返回
7
(
CFString
)。
注意:重新定义的$.CFGetTypeID()
返回JS的Number
实例,而原始返回底层数字的字符串表示形式(CFTypeID
值)。
一般来说,
如果您想非正式地知道给定CF*
实例的具体类型,请使用CFShow()
,例如:
$.CFShow($.kUTTypeHTML) // -> '{\n type = "{__CFString=}";\n}'
注意:
CFShow()
没有返回值,而是直接打印到
stderr,因此您无法在 JS 中捕获输出。
您可以使用
ObjC.bindFunction('CFShow', ['void', [ 'void *' ]])
重新定义
CFShow
,以便不显示包装字典。
对于本地识别的 CF* 类型 - 那些映射到 JS 原语的类型 - 您将直接看到特定类型(例如,
false
的
CFBoolean
);对于未知的 - 因此被包装的 - 实例,您将看到上面的包装结构 - 继续阅读获取更多信息。
[1] 运行以下代码可以让你对 JXA 生成的包装对象(wrapper object)有一个大致的概念,当传递一个未知类型时:
// 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 ))
返回 {"type":"{__CFString=}"}
,即一个JS对象,其属性 type
的值是-在ObjC桥接调用“来回路程”后-仅仅是原始 CFString
实例的一个字符串表示。
这个解决方案包含一个有用的代码片段,可以将CF*
实例的类型名称作为字符串获取。
如果我们将其重构为一个函数并应用必要的CFGetTypeID()
重新定义,我们将得到以下结果,但是:
- 需要hack才能使其可预测地返回一个值(请参见注释和源代码)
- 即使如此,有时也会在返回的字符串末尾出现随机字符,例如
CFString,
而不是CFString
。
如果有人能够解释为什么需要hack以及随机字符来自何处,请告诉我。问题可能与内存管理有关,因为CFCopyTypeIDDescription()
和CFStringCreateExternalRepresentation()
都返回一个调用者必须释放的对象,我不知道JXA是否会这样做。
function getCFTypeName(cfObj) {
ObjC.bindFunction('CFGetTypeID', [ 'unsigned long', [ 'void *' ]])
do {
var data = $.CFStringCreateExternalRepresentation(
null,
$.CFCopyTypeIDDescription($.CFGetTypeID(cfObj)),
0x08000100,
0
);
s = $.CFDataGetBytePtr(data)
} while (s === undefined)
return s
}
CFMakeCollectable
使新分配的 Core Foundation 对象有资格进行垃圾回收。在非垃圾回收进程中调用它没有任何效果,只会返回传递给它的对象。 - bacongravy$.kUTTypeHtml
是Ref
的一个实例。在Ref.prototype
上定义的所有函数和属性都可用于Ref
的实例上。要了解更多信息,请搜索“JavaScript原型继承链”的相关信息。 - bacongravyCFMakeCollectable
没有任何效果。它的无效并不取决于传递给它的参数类型,无论是常量还是其他类型。在我的测试中,它似乎是安全的。 - bacongravyotool -oV /System/Library/PrivateFrameworks/JavaScriptAppleEvents.framework | tail -3
。有关更多信息,请参见此问题。 - bacongravyotool -oV /System/Library/PrivateFrameworks/JavaScriptAppleEvents.framework/JavaScriptAppleEvents | tail - 3
)。 - mklement0