gpt4 book ai didi

objective-c - JXA:从CoreServices访问CFString常量

转载 作者:搜寻专家 更新时间:2023-10-30 20:02:17 25 4
gpt4 key购买 nike

JXA及其内置的objc桥通过Foundation对象自动公开$框架中的枚举和常量;例如:

$.NSUTF8StringEncoding  // -> 4

但是,在未自动导入的低级api中也有一些有用的 CFString常量,即 kUTType*中定义常用 UTI值的 CoreServices常量,例如uti kUTTypeHTML"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)不起作用,也不起作用。
我想知道:
如果有一个本地JXA方法来做这件事,我就不知道了。
否则,如果不需要使用 ObjC.unwrap($.kUTTypeHTML[0])来定义可以解决问题的 .deepUnwrap()函数的绑定,例如 ObjC.bindFunction()CFString*(),但如何翻译objc签名对我来说并不明显。

最佳答案

虽然我不明白所有的含义,但以下似乎是可行的:

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

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

kUTType*常量被定义为 CFStringRefCFStringGetCStringPtr以指定的编码返回 CFString对象的内部c字符串(如果可以提取 "with no memory allocations and no copying, in constant time"-或者 NULL否则)。
对于内置的常量,似乎总是返回一个c字符串(而不是 NULL),通过c数据类型映射到jxa数据类型,它可以直接在javascript中使用:
 $.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是我们的出发点: $.kUTTypeHTMLCFString[Ref]类型,但是JXA不返回它的JS字符串表示,只有 [object Ref]
注意:以下部分是推测性的-如果我错了一定要告诉我。
不识别 CFString还有另一个副作用,即调用接受泛型类型的 CF*()函数(或者调用接受jxa不知道的免费桥接 CF*类型的cocoa方法):
在这种情况下,如果参数类型与调用函数的参数类型不完全匹配,JXA明显地将输入对象隐式地封装在 CFDictionary实例中,其唯一条目具有键 type,关联值包含原始对象。
这大概就是上述 $.NSString.stringWithString()调用失败的原因:它是通过 CFDictionary包装器而不是 CFString实例传递的。
另一个例子是 CFGetTypeID()函数,它需要一个 CFTypeRef参数:即任何 CF*类型。
由于JXA不知道将 CFStringRef参数作为 CFTypeRef参数传递是错误的,它错误地执行了上述包装,并有效地通过了 CFDictionary实例而不是:
$.CFGetTypeID($.kUTTypeHTML) // -> !! 18 (CFDictionary), NOT 7 (CFString)

这就是 houthakker中的 his solution attempt体验。
对于给定的 CF*函数,可以使用 ObjC.bindFunction()重新定义感兴趣的函数,从而绕过默认行为:
// Redefine CFGetTypeID() to accept any type as-is:
ObjC.bindFunction('CFGetTypeID', ['unsigned long', [ 'void *']])

现在, $.CFGetTypeID($.kUTTypeHTML)正确返回 7CFString)。
注意:重新定义的 $.CFGetTypeID()返回js Number实例,而原始返回一个基础数的字符串表示( CFTypeID value)。
一般来说,如果您想非正式地知道给定的 CF*实例的特定类型,请使用 CFShow(),例如:
$.CFShow($.kUTTypeHTML) // -> '{\n    type = "{__CFString=}";\n}'

注意: CFShow()不返回任何内容,而是直接打印到stderr,因此无法在js中捕获输出。
您可以用 CFShow重新定义 ObjC.bindFunction('CFShow', ['void', [ 'void *' ]]),以免显示包装字典。
对于本地识别的CF*类型——映射到JS原语的那些类型——您将直接看到特定类型(例如, CFBooleanfalse);对于未知的并因此包装的实例,您将看到上述包装器结构。
[ 1 ]运行以下内容,您将了解包装器对象在传递未知类型时正在生成的包装对象:
// 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)

类似地,使用已知的方法来表示< > >和< > >的等效性,
ObjC.deepUnwrap($.NSDictionary.dictionaryWithDictionary( $.kUTTypeHTML ))

返回一个js对象,也就是一个JS属性,它的值是在这一点上,在一个未经调用的桥接调用返回后,仅仅是一个字符串表达式,大概是原始的cc实例。
houthakker's solution attempt还包含一个代码片段,用于将 NSDictionary实例的类型名作为字符串获取。
如果我们将它重构为一个函数并应用 CFDictionary的必要重新定义,我们将得到以下结果:
需要一个hack来使其返回一个可预测的值(参见注释和源代码)
即使这样,随机字符有时也会出现在返回的字符串结束时,例如 {"type":"{__CFString=}"}而不是 type
如果有人有一个解释,为什么需要黑客和从哪里来的随机字符,请让我知道。这些问题可能是内存管理相关的,因为 CFStringCF*都返回调用方必须释放的对象,并且我不知道JXA是否/如何/何时进行。
/* 
Returns the type name of the specified CF* (CoreFoundation) type instance.
CAVEAT:
* A HACK IS EMPLOYED to ensure that a value is consistently returned f
those CF* types that correspond to JS primitives, such as CFNumber,
CFBoolean, and CFString:
THE CODE IS CALLED IN A TIGHT LOOP UNTIL A STRING IS RETURNED.
THIS SEEMS TO WORK WELL IN PRACTICE, BUT CAVEAT EMPTOR.
Also, ON OCCASION A RANDOM CHARACTER APPEARS AT THE END OF THE STRING.
* Only pass in true CF* instances, as obtained from CF*() function
calls or constants such as $.kUTTypeHTML. Any other type will CRASH the
function.

Example:
getCFTypeName($.kUTTypeHTML) // -> 'CFString'
*/
function getCFTypeName(cfObj) {

// Redefine CFGetTypeID() so that it accepts unkown types as-is
// Caution:
// * ObjC.bindFunction() always takes effect *globally*.
// * Be sure to pass only true CF* instances from then on, otherwise
// the function will crash.
ObjC.bindFunction('CFGetTypeID', [ 'unsigned long', [ 'void *' ]])

// Note: Ideally, we'd redefine CFCopyDescription() analogously and pass
// the object *directly* to get a description, but this is not an option:
// ObjC.bindFunction('CFCopyDescription', ['void *', [ 'void *' ]])
// doesn't work, because, since we're limited to *C* types, we can't describe
// the *return* type in a way that CFStringGetCStringPtr() - which expects
// a CFStringRef - would then recognize ('Ref has incompatible type').

// Thus, we must first get a type's numerical ID with CFGetTypeID() and then
// get that *type*'s description with CFCopyTypeIDDescription().
// Unfortunately, passing the resulting CFString to $.CFStringGetCStringPtr()
// does NOT work: it yields NULL - no idea why.
//
// Using $.CFStringCreateExternalRepresentation(), which yields a CFData
// instance, from which a C string pointer can be extracted from with
// CFDataGetBytePtr(), works:
// - reliably with non-primitive types such as CFDictionary
// - only INTERMITTENTLY with the equivalent types of JS primitive types
// (such as CFBoolean, CFString, and CFNumber) - why??
// Frequently, and unpredictably, `undefined` is returned.
// !! THUS, THE FOLLOWING HACK IS EMPLOYED: THE CODE IS CALLED IN A TIGHT
// !! LOOP UNTIL A STRING IS RETURNED. THIS SEEMS TO WORK WELL IN PRACTICE,
// !! BUT CAVEAT EMPTOR.
// Also, sometimes, WHEN A STRING IS RETURNED, IT MAY CONTAIN A RANDOM
// EXTRA CHAR. AT THE END.
do {
var data = $.CFStringCreateExternalRepresentation(
null, // use default allocator
$.CFCopyTypeIDDescription($.CFGetTypeID(cfObj)),
0x08000100, // kCFStringEncodingUTF8
0 // loss byte: n/a here
); // returns a CFData instance
s = $.CFDataGetBytePtr(data)
} while (s === undefined)
return s
}

关于objective-c - JXA:从CoreServices访问CFString常量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33314614/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com