Objective c JXA:从CoreServices访问CFString常量
使用内置的ObjC桥,通过Objective c JXA:从CoreServices访问CFString常量,objective-c,core-services,javascript-automation,Objective C,Core Services,Javascript Automation,使用内置的ObjC桥,通过$对象自动公开基础框架中的枚举和常量;e、 g: $.NSUTF8StringEncoding // -> 4 但是,在较低级别的API中也有一些有用的、不会自动导入的CFString常量,即CoreServices中定义常用值的kUTType*常量,例如UTI“public.html” 虽然您可以使用ObjC.import('CoreServices')导入它们,但它们的字符串值(不容易)访问,可能是因为其类型是CFString[Ref]: ObjC.imp
$
对象自动公开基础
框架中的枚举和常量;e、 g:
$.NSUTF8StringEncoding // -> 4
但是,在较低级别的API中也有一些有用的、不会自动导入的CFString
常量,即CoreServices
中定义常用值的kUTType*
常量,例如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()
函数定义可以解决问题的绑定,例如to或,但我不清楚如何转换ObjC签名CFString*()
虽然我不理解所有的含义,但以下似乎有效:
$.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($.KuttypeThypHTML).js
,验证JXA不知道CFString
和NSString
的等价性,该命令应返回输入字符串的副本,但由于-[\uu NSDictionaryM length]:无法识别的选择器发送到实例而失败
不识别CFString
是我们的出发点:$。KuttypeTHtml
的类型是CFString[Ref]
,但JXA不返回它的JS字符串表示,只返回[object Ref]
注意:以下部分是推测性的——如果我错了,一定要告诉我
不识别CFString
还有另一个副作用,即调用接受泛型类型的CF*()
函数(或接受JXA不知道的免费桥接CF*
类型的Cocoa方法):
在这种情况下,如果参数类型与调用函数的参数类型不完全匹配,JXA显然会隐式地将输入对象包装在CFDictionary
实例中,该实例的唯一条目具有keytype
,关联值包含原始对象。[1]
这大概就是上面的$.NSString.stringWithString()
调用失败的原因:它被传递给CFDictionary
包装器,而不是CFString
实例
另一个例子是CFGetTypeID()
函数,它需要一个CFTypeRef
参数:即任何CF*
类型
由于JXA不知道传递CFStringRef
参数作为CFTypeRef
参数是可以的,因此它错误地执行了上述包装,实际上传递了一个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
(CFString
)
注意:重新定义的$.CFGetTypeID()
返回一个JSNumber
实例,而原始的返回一个底层数字的字符串表示形式(CFTypeID
值)
通常,如果您想非正式地了解给定CF*
实例的特定类型,请使用CFShow()
,例如:
$.CFShow($.kUTTypeHTML) // -> '{\n type = "{__CFString=}";\n}'
注意:CFShow()
不返回任何内容,而是直接打印到stderr,因此无法在JS中捕获输出。
您可以使用ObjC.bindFunction('CFShow',['void',['void*']])重新定义CFShow
,以便不显示包装器字典
对于本机识别的CF*类型(映射到JS原语的类型),您将直接看到特定的类型(例如,CFBoolean
Forfalse
);对于未知的(因此是包装的)实例,您将看到如上所述的包装器结构-请继续阅读以了解更多信息
[1] 运行以下命令可以了解传递未知类型时JXA生成的包装器对象:
// 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)
类似地,使用NSDictionary
和CFDictionary
的已知到JXA的等价性
ObjC.deepUnwrap($.NSDictionary.dictionaryWithDictionary( $.kUTTypeHTML ))
返回{“type”:“{uuu CFString=}”
,即属性为type
的JS对象,其值在ObjC桥调用往返之后,仅为原始CFString
实例的字符串表示形式
ObjC.deepUnwrap($.NSDictionary.dictionaryWithDictionary( $.kUTTypeHTML ))
ObjC.bindFunction('CFMakeCollectable', [ 'id', [ 'void *' ] ]);
var cfString = $.CFStringCreateWithCString(0, "foo", 0); // => [object Ref]
var nsString = $.CFMakeCollectable(cfString); // => $("foo")
Ref.prototype.toNS = function () { return $.CFMakeCollectable(this); }
ObjC.import('CoreServices')
$.kUTTypeHTML.toNS() // => $("public.html")