同样的代码,在编译通过的情况下,运行时会发生这个EXC_BAD_ACCESS,EXC_I386_GPFLT的崩溃。
这是Swift3.3,Swfit4.1才发生,在Swift3上面并没有这个问题。
简单感受就是Swift的泛型和NSObject还没那么合拍,本文最后有重现这个问题的最小代码。
虽然不至于又要提“什么是最好的编程语言”这个梗,但是翻版同样代码到Java和.Net还不至于报错。当然Java,.Net也没有NSObject这个东西。至少鄙人还是求实和不怕麻烦的精神做了跨语言的对比工作以及不厌其烦的剥离出问题最小重现代码做了点贡献
先来张报错图,有个直观认识
EXC_I386_GPFLT
它发生需要4个条件,
如此苛刻的条件,Swift没有测试到位而是个Bug的可能性比较大;
破除任何一个条件即可解除这个麻烦。当然看你适合破除哪个代价最小。一般来说去掉第2条比较容易。
Swift不是鄙人主力编程语言,也没有ObjectC深厚的积累,如果有高手可以指点出根本原因,还望赐教。私以为,这个问题还是应该在Swift3.3的更新说明里给出该有的强力依据才能让人心服口服了。
1.用了含有associatedtype的protocol
public protocol LineCutter {
associatedtype LP : GPoint //触发条件1
}
2.有继承于NSObject
public class NSObjectLine<P> : NSObject where P : GPoint { //触发条件2
}
3.定义的泛型类的类型参数和之前protocol的associatedtype形成了约束。
看下面的 L.LP == P
public class Calculator<P,L> where L:LineCutter, L.LP == P { //触发条件3
4.呼叫了含有第三条约束的方法并再次有约束类型参数
估计这条是压死Swift3.3的最后一根稻草了
看下面的 where K:NSObjectLine<P>
public func test1<K>(stroke:K) where K:NSObjectLine<P>{ //触发条件4
让这个问题重现最小测试代码
要重现这个问题,只要新建一个Swift控制台程序,把内容全部贴到main.swift里,立即运行就能体会到最开始截图的那一幕了。如下
import Foundation
public protocol LineCutter {
associatedtype LP : GPoint //触发条件1
}
public class TLsCutter : LineCutter {
public typealias LP = TPoint
}
public protocol GPoint {
}
public struct TPoint : GPoint {
}
//----------------------------------------------------
public struct StructLine<P> where P : GPoint {
}
public class NSObjectLine<P> : NSObject where P : GPoint { //触发条件2
}
public class AnyLine<P> : Any where P : GPoint {
}
//----------------------------------------------------
public class Calculator<P,L> where L:LineCutter, L.LP == P { //触发条件3
public init() {
}
public func test0(){
let all = NSObjectLine<P>()
print("\(type(of:all))")
}
public func test1<K>(stroke:K) where K:NSObjectLine<P>{ //触发条件4
//public func test1<K>(stroke:K) where K:AnyLine<P>{
let x1 = ContiguousArray<P>()
print("\(type(of:x1))")
//运行时报错 EXC_BAD_ACCESS(code=EXC_I386_GPFLT)
let x2 = StructLine<P>()
print("\(type(of:x2))")
}
}
let eqbc = Calculator<TPoint,TLsCutter>()
eqbc.test0()
eqbc.test1(stroke: NSObjectLine<TPoint>())
//eqbc.test1(stroke: AnyLine<TPoint>())
破除的一个方式
1.把test1方法声明(关键就是where后面那个类 不能继承于NSObject)
public func test1<K>(stroke:K) where K:NSObjectLine<P>{
用下面的替代
public func test1<K>(stroke:K) where K:AnyLine<P>{
2.同步的修改调用处
eqbc.test1(stroke: NSObjectLine<TPoint>())
用
eqbc.test1(stroke: AnyLine<TPoint>())
代替
网友评论