【问题标题】:Runtime error when using CoreFoundation objects in a swift NSObject subclass在 swift NSObject 子类中使用 CoreFoundation 对象时出现运行时错误
【发布时间】:2014-09-19 18:37:21
【问题描述】:

这是一个非常简单的类(NSObject 的子类),它保留了一个 CGPath 对象列表并将一个 CGPath 附加到 init 上的数组中:

import Foundation
class MyClass: NSObject {

    var list = [CGPath]();

    init() {
        list.append(CGPathCreateMutable());
    }
}

尝试使用此类时:

var instance = MyClass();
println(instance.list.count); // runtime error after adding this line

产生丑陋的崩溃:

Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
* thread #1: tid = 0x1251d1, 0x00000001064ce069 libswiftFoundation.dylib`partial apply forwarder for Swift._ContiguousArrayBuffer.(_asCocoaArray <A>(Swift._ContiguousArrayBuffer<A>) -> () -> Swift._CocoaArray).(closure #1) with unmangled suffix "392" + 121, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  * frame #0: 0x00000001064ce069 libswiftFoundation.dylib`partial apply forwarder for Swift._ContiguousArrayBuffer.(_asCocoaArray <A>(Swift._ContiguousArrayBuffer<A>) -> () -> Swift._CocoaArray).(closure #1) with unmangled suffix "392" + 121
    frame #1: 0x00000001064ce0d8 libswiftFoundation.dylib`partial apply forwarder for reabstraction thunk helper <T_0_0> from @callee_owned (@in T_0_0) -> (@owned Swift.AnyObject) to @callee_owned (@in T_0_0) -> (@out Swift.AnyObject) with unmangled suffix "395" + 56
    frame #2: 0x00000001057bf29a libswift_stdlib_core.dylib`Swift.MapSequenceGenerator.next <A : Swift.Generator, B>(@inout Swift.MapSequenceGenerator<A, B>)() -> Swift.Optional<B> + 554
    frame #3: 0x00000001057bf49a libswift_stdlib_core.dylib`protocol witness for Swift.Generator.next <A : Swift.Generator>(@inout Swift.Generator.Self)() -> Swift.Optional<Swift.Generator.Self.Element> in conformance Swift.MapSequenceGenerator : Swift.Generator + 58
    frame #4: 0x00000001064d8e97 libswiftFoundation.dylib`Swift._copyCollectionToNativeArrayBuffer <A : protocol<Swift._Collection, Swift._Sequence_>>(A) -> Swift._ContiguousArrayBuffer<A.GeneratorType.Element> + 1511
    frame #5: 0x00000001064f1951 libswiftFoundation.dylib`protocol witness for Swift.Sequence.~> @infix <A : Swift.Sequence>(Swift.Sequence.Self.Type)(Swift.Sequence.Self, (Swift._CopyToNativeArrayBuffer, ())) -> Swift._ContiguousArrayBuffer<Swift.Sequence.Self.GeneratorType.Element> in conformance Swift.LazyRandomAccessCollection : Swift.Sequence + 449
    frame #6: 0x00000001064daf7b libswiftFoundation.dylib`Swift.ContiguousArray.map <A>(Swift.ContiguousArray<A>)<B>((A) -> B) -> Swift.ContiguousArray<B> + 1339
    frame #7: 0x00000001064da9cb libswiftFoundation.dylib`Swift._ContiguousArrayBuffer._asCocoaArray <A>(Swift._ContiguousArrayBuffer<A>)() -> Swift._CocoaArray + 475
    frame #8: 0x00000001064ced3e libswiftFoundation.dylib`Swift._ArrayBuffer._asCocoaArray <A>(Swift._ArrayBuffer<A>)() -> Swift._CocoaArray + 78
    frame #9: 0x000000010649f583 libswiftFoundation.dylib`Foundation._convertArrayToNSArray <A>(Swift.Array<A>) -> ObjectiveC.NSArray + 35
    frame #10: 0x000000011163b40e

第 9 帧引起了我的注意:libswiftFoundation.dylib\`Foundation._convertArrayToNSArray。为什么 swift 会尝试将我漂亮、快乐的 Swift 数组转换为 NSArray

仅当在数组中使用 CFType 对象时才会出现此问题。我可以在数组中使用NSObject 子类就好了(例如[UIBezierPath]

这个问题可以通过不继承 NSObject 轻松解决,但是我想知道 swift 到底对我无辜的数组做了什么另外,我如何将still use NSObject 作为基类并拥有CFType 对象的数组,例如CGPath

还指出 (谢谢,@user102008!),它不必是 NSObject 的子类,但该属性只需声明为 @objc

some documentation 的目的是使用 @objc 并在 Swift 中继承一个 Objective-C 类:

当你定义一个继承自 NSObject 或任何其他的 Swift 类时 Objective-C 类,该类自动兼容 Objective-C。

但是,我正在尝试在 Swift 中使用我的 Swift 类。当子类化一个 Objective-C 类并在 Swift 中使用它时,文档中没有提到不同行为的副作用。但文档还提到将 Swift 数组桥接到NSArray

当您从 Swift 数组桥接到 NSArray 对象时,元素 在 Swift 数组中必须与AnyObject 兼容。

接着说:

如果 Swift 数组中的元素与 AnyObject 不兼容,则在桥接到 NSArray 对象时会出现运行时错误。

嗯,CGPathAnyObject 不兼容,但 Swift不应该试图将我的 Swift 数组转换为 NSArray

【问题讨论】:

  • 顺便说一句:该类甚至不必继承NSObject;只需声明该属性@objc 即可发生此问题
  • @user102008 不错的收获!
  • “CGPath 与 AnyObject 不兼容” 是什么让你这么说?它似乎适合我。
  • @newacct 我猜真的是 CGPath 与 NSObject 不兼容,这意味着它不能在 NSArray 中。
  • 更新:这已在 Xcode 6 beta 5 中修复

标签: ios arrays swift nsobject


【解决方案1】:

嗯,CGPath 与 AnyObject 不兼容,但 Swift 不应该尝试将我的 Swift 数组转换为 NSArray。

它必须。您说您希望它与 ObjC 兼容,而 ObjC 不能直接处理 Swift 数组。所以它必须将其转换为 NSArray。

简短的回答是,这完全按照记录的方式工作。由于这是 iOS,因此解决方案是微不足道的。只需切换到 UIBezierPath(与 AnyObject 兼容)而不是 CGPath。

【讨论】:

  • 所以即使我没有在 Objective-C 中使用它,它也会(尝试)将其转换为 NSArray?我只是在 Swift 中调用 println(MyClass().list.count);,这似乎没有必要转换......
  • 您说您希望它与 ObjC 兼容。它在编译时做出决定。否则,每次您请求它时,它都必须在运行时执行可能非常昂贵的转换(可能会失败)。如果您真的想要这样的东西,请创建一个 objc 计算属性来为您转换。
  • 这里的关键是要意识到将 @objc 放在前面(或从 NSObject 继承)表示“我希望你以与 ObjC 兼容的方式存储它,我保证我不会使用任何破坏这一点的功能。”
  • 更重要的是它在 Swift 中崩溃是一种祝福。最糟糕的答案是让它在 Swift 中工作,然后在 ObjC 中中断(因为您显然希望它在 ObjC 中工作,或者您不会以这种方式声明它)。您希望那些将失败的事情可靠地失败。你不会希望 Swift “尽力而为”,有时会成功有时会失败。
  • 这是AnyObject 的文档定义吗?我同意这应该AnyObject的定义,但我不知道它目前在Swift中。
【解决方案2】:

Swift 中与 Objective-C 兼容的所有对象都使用完整的 Objective-C 运行时。这意味着当您创建一个继承自 NSObject(或定义为与 Objective-C 兼容)的类型时,其方法和属性使用 Messaging 而不是在编译时链接到您的方法。

要接收消息,所有纯粹的 Swift 对象,比如你的数组,都必须转换为它们的 Objective-C 对应部分。无论您当前是否在 Objective-C 中使用该对象,都会发生这种情况,因为无论如何,它都使用消息传递。

因此,如果你创建一个继承自 NSObject 的类,你必须假设所有属性都可以转换为 Objective-C 的对应物。正如您在问题中所说,您可以使用UIBezierPath 而不是CGPath 来实现这一点。

【讨论】:

  • 在 Beta 4 编译器中,您可以通过将属性声明为 final 来消除崩溃,这告诉 Swift 不要使用消息传递来访问该属性。相反,Beta 5 编译器不再使用消息传递来访问属性,因此它会成功 - 除非您将属性声明为 dynamic,这会再次触发崩溃。
【解决方案3】:

来自“在 Cocoa 和 Objective-C 中使用 Swift”

当您在 Objective-C 代码中使用 Swift 类或协议时, importer 将导入的 API 中任何类型的所有 Swift 数组替换为 NSArray。

所以按照这个,数组不应该转换成NSArray。事实上,我也认为你应该提交一份错误报告。

与此同时,您可以使用相应的 Cocoa-Class 或将您的 CGPath 包装在一个额外的对象中。

【讨论】:

  • 我不明白你怎么能根据那篇文档推断不应该转换 Swift 数组。我在其他 Swift 代码中使用我的 Swift 类。
  • 这就是我想说的。它绝对不应该被转换。在 Swift 编译器中,还有几个 bug。我想这是其中之一。
  • 该文本不限制 Swift 代码调用其他 Swift 代码时会发生什么,仅当 Objective-C 代码调用 Swift 代码时。
【解决方案4】:
import UIKit

class MyClass: NSObject {

    var list = [CGPath]();

    override init() {
        list.append(CGPathCreateMutable());
    }
}

var instance = MyClass()
print(instance.list.count) // 1

【讨论】:

  • 哦,他们解决了吗??
【解决方案5】:

这可能是你没有导入Foundaion,这是NSObject所在的位置:

import Foundation

这是包含 NSObject 的地方(据我所知)。希望这有帮助。 :)

【讨论】:

  • 不。这没有帮助。我已将其添加到我的问题示例中。无论如何,“我不知道 x 类在哪里”错误可能是编译器错误,而不是我遇到的运行时错误。
  • 那么对不起。我没有程序员那样的经验,所以我可能会犯错误。 ://
  • 如果证明答案不能解决问题,则应删除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多