如何从 NativeScript 调用 Objective-C NSExpression(format: ....)?

Posted

技术标签:

【中文标题】如何从 NativeScript 调用 Objective-C NSExpression(format: ....)?【英文标题】:How to call Objective-C NSExpression( format: ....) from NativeScript? 【发布时间】:2019-02-26 17:10:38 【问题描述】:

我正在努力扩展NativeScript-Mapbox plugin 以包含渲染圆圈的功能,该圆圈会随着地图的缩放而调整大小。我正在使用example on this page。

ios 上,我在尝试将此 NSExpression 调用转换为 NativeScript 时遇到问题:

layer.circleRadius = [NSExpression expressionWithFormat: @"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)",
@@12: @2, @22: @180];

在我看来,tns-platform-declarations ..objc!Foundation.d.ts NSExpression 定义中列出的此调用的 NativeScript 类似物是:

static expressionWithFormatArgumentArray(expressionFormat: string, _arguments: NSArray<any>): NSExpression;

这是正确的方法吗?

如果是,如何将 [12: 2, 22: 180] 转为 NSArray 类型?

缺少文档,我尝试创建一个数组数组并将其传递给:

new NSArray( objects: myArray );

但它会因段错误而严重崩溃。

显然,我错过了一些东西。

【问题讨论】:

【参考方案1】:

您正在处理的 swift(不是 Objective-c)对象不是数组,而是字典。

import Swift
print(type(of: [12: 2, 22: 180]))
// Dictionary<Int, Int>

它所期望的数组是你想要的所有格式替换参数的数组;在这种情况下,该数组的长度为1,并且只包含字典。你可以把它翻译成你的 NativeScript javascript/TypeScript:

const expr = NSExpression.expressionWithFormatArgumentArray(
  "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)",
  [12: 2, 22: 180]);

【讨论】:

打字稿编译器给了我一个错误。 ' 12: number; 类型的参数22: number;[]' 不能分配给 NSArray 的类型。缺少属性计数。在条目中添加 count: 并不能修复编译错误。 我升级了 Typescript 和核心模块,并开始编译。但是,它崩溃了,我不明白为什么。【参考方案2】:

手动创建一个 Native iOS 对象;几乎总是这样:

const x = NSArray.alloc().init();'
or
const y = new NSArray();

但是有几个注意事项;

    您几乎总是希望使用NSMutableArray() 而不仅仅是普通的NSArray,因为 NSArray 是静态的;而 Mutable 意味着你可以改变它。由于您是在 JS 中创建它;您必须在创建后为其添加值,因此 Mutable 是您几乎总是需要的类型。 大多数情况下可以使用new NSMutableArray();;但是,我遇到了 new 在某些本机 iOS 对象上与 .alloc().init() 无法正常工作的情况,即使 new 基本上是 .alloc().init() 的包装器。我建议你总是做 .alloc().init() 只是为了保持一致。; 如果iOS函数调用专门采用NSArray;然后你可以做这样的事情: const myNSArray = NSArray.arrayWithArray([1,2,3,4,5]); 在这个例子中; myNSArray 现在是一个 NSArray 对象,[1,2,3,4,5] 最初是我们传入的一个 JS 数组对象。默认情况下,调用任何需要 NSArray 的 iOS 函数都会自动将任何 JavaScript 数组转换为 NSArray。大多数数据类型的封送处理会自动为您完成。 如果 iOS 函数没有有效类型(即 vardef 类型);或者如果函数具有可变数量的参数;那么 NativeScript 可能根本无法调用它,因此在您尝试时会崩溃并出现错误。

最后,如果您对如何在 NativeScript 中将 JS 处理到 iOS(或 android)有其他问题;我建议您在 http://searchcode.nativescript.rocks 上搜索该关键字,然后仔细阅读它找到的使用这些特定关键字的源代码。

至于问题的NSExpression部分

const expr = NSExpression.expressionWithFormatArgumentArray(
  "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)",
  NSArray.arrayWithArray([NSDictionary.dictionaryWithDictionary(12: 2, 22: 180)]));

因为 Array 没有特定的对象类型可以放入其中,所以将我们放入其中的对象类型强制为 NSDictionary 会更安全,因为这是 iOS 所期望的。为了确保传递给expressWithFormatArgumentArray 的是一个NSArray,我们使用NSArray.arrayWithArray() 调用来包装我们创建的JSArray。您可能只需执行 [] 就可以逃脱惩罚,但 TS 有时讨厌不知道事物的类型。

【讨论】:

感谢您抽出宝贵时间如此彻底地回答我的问题。非常感谢。但是,我遇到了一个我不理解的编译时问题:mapbox.ios.ts:1427:26 - 错误 TS2345:'any[]' 类型的参数不可分配给'NSArray' 类型的参数。 “any[]”类型中缺少属性“count”。第1427章 NSArray.arrayWithArray([NSDictionary.dictionaryWithDictionary(12: 2, 22: 180)])); 和 ``` mapbox.ios.ts:1427:66 - 错误 TS2345: 类型参数 ' 12: number; 22:数字; ' 不可分配给“NSDictionary”类型的参数。对象字面量只能指定已知属性,而 '12' 不存在于类型 'NSDictionary' 中。第1427章``` 在此处查看问题线程中的讨论:github.com/NativeScript/NativeScript/issues/6971 即使该讨论中的链接示例 (github.com/NickIliev/NS-Issues-2019-I/blob/master/NativeScript/…) 也不起作用。我想知道我是否有可能的配置问题? 我已验证以下 Objective-C 在测试应用程序中按预期工作: layer.circleRadius = [NSExpression expressionWithFormat: @"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential' , 1.75, %@)", @@12: @2, @22: @180];但是,相应的本机脚本编译但会导致崩溃: const expr = NSExpression.expressionWithFormatArgumentArray("mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)", [12: 2, 22 : 180]);【参考方案3】:

好的,只是为了循环并完成这个线程,我 /finally/ 想出了如何让它在不崩溃的情况下工作。

该方法需要一个包含 NSDictionary 类型的数组,并且根据 this issue 和 this one,NSDictionary 的 NativeScript 类型定义似乎不正确。

所以经过几天的反复试验,转换了 Objective-C 调用:

layer.circleRadius = [NSExpression expressionWithFormat: @"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)",
@@12: @2, @22: @180];

进入 TypeScript/NativeScript 是完全违反直觉的:

let nsDict = new (NSDictionary as any)( [ 2, 180 ], [ 12, 22 ] );
let nsArray = NSArray.arrayWithArray( [ nsDict ] );

layer.circleRadius = NSExpression.expressionWithFormatArgumentArray(
  "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)", nsArray );

这编译并工作。请注意,键位于值之后的单独数组中,而不是相反。

如果您使用上面建议的 NSDictionary 方法,则会出现编译错误,因为显然输入不支持该表示法。

升级到最新的 NativeScript 和核心模块确实允许它编译建议的 NSArray() [] 引用,但生成的代码会导致异常:,即:

const expr = NSExpression.expressionWithFormatArgumentArray( "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.75, %@)", [12: 2, 22: 180]);

异常崩溃。

据我所知,该数组未正确编组到 NSDictionary 实例中。同样使用 NSArray.arrayWithArray( [ ... ] );

【讨论】:

以上是关于如何从 NativeScript 调用 Objective-C NSExpression(format: ....)?的主要内容,如果未能解决你的问题,请参考以下文章

NativeScript-Vue - 从外部组件导航

Nativescript-Vue 超时后关闭模式

如何通过框架从 NativeScript 模式开启器获取上下文?

如何从 NativeScript 中的 Image 获取文件并将其上传到服务器

如何在 nativescript vue 中从主页导航到登录页面?

如何从java调用javascript函数?