Swift - 向下转换一系列协议以使用unsafeBitCast致命错误键入崩溃

bar*_*bus 1 ios swift swift2

我正在尝试构建一个协议类型的"过滤"数组

我有一系列符合几种不同协议的结构(评估,级别和门) - 可分级,可重复和可测试:

protocol Stageable
{
    var index : Int { get }
    var steps : [Step] { get }
}

protocol Testable
{
    var threshold : Float { get }
}

protocol Repeatable
{
    var sessions: Int { get }
}

struct Gate : Stageable, Testable, Repeatable
{
    private(set) var index : Int
    private(set) var steps : [Step]

    private(set) var threshold : Float

    private(set) var sessions : Int
}

struct Assessment : Stageable, Testable
{
    private(set) var index : Int
    private(set) var steps : [Step]

    private(set) var threshold : Float
}

struct Level : Stageable, Repeatable
{
    private(set) var index : Int
    private(set) var steps : [Step]

    private(set) var sessions : Int
}
Run Code Online (Sandbox Code Playgroud)

Step是另一个结构.没有使用任何类.

这些结构在添加到数组之前就已填充.数组通常采用[Assessment,Gate,Level,Level]的形式.所有结构数据都从XML文件填充.

在某些情况下,我只想查看数组中的"级别",所以我这样做:

// stages = [Assessment, Gate, Level, Level, ...]
let levels = stages.filter{ $0 is Level }
Run Code Online (Sandbox Code Playgroud)

如果我查询它,它看起来很好,例如levels.count是我所期望的.但是,如果我现在想要将数组转换为[Level],则会因以下错误而崩溃:

fatal error: can't unsafeBitCast between types of different sizes
Run Code Online (Sandbox Code Playgroud)

这是因为我从一个协议转换为结构类型?我也觉得我已经错过了协议的主要好处,必须有更好的方法来做到这一点.

目前正在使用Xcode 7 beta 5.

hen*_*nes 6

构造结构数组是有问题的,因为结构是值类型.这意味着结构数组的每个元素占用内存中结构的大小.这对于标准对象的数组是不同的,因为它们通过引用传递.对象数组中的每个元素都是一个引用(指向特定内存区域的指针).

为了证明这一点,请考虑以下内容

class ABC {
    private var i = 0
    private var j = 1
    private var k = 2
}

print(sizeof(UIViewController))
print(sizeof(UIImage))
print(sizeof(NSObject))
print(sizeof(ABC))
Run Code Online (Sandbox Code Playgroud)

我的平台上的每个print语句输出8都是内存地址的大小(显然不同于此类实例占用的内存量).

另一方面,当我从您的问题中获取代码并执行此操作时

print(sizeof(Stageable))
print(sizeof(Level))
Run Code Online (Sandbox Code Playgroud)

我得到4024其分别是在内存中,这些结构的情况下的实际尺寸.这意味着类型数组[Stageable]包含40个字节元素的块,而类型数组[Level]包含24个字节元素的块.因此,您无法在这些数组类型之间进行转换,因为这需要重写数组的内存.

作为替代方法,您可以使用该map方法强制进行类型转换:

let levels = stages.filter({ $0 is Level }).map({ $0 as! Level })
Run Code Online (Sandbox Code Playgroud)

通过利用以下flatMap方法也可以简化上述内容:

let levels = stages.flatMap({ $0 as? Level })
Run Code Online (Sandbox Code Playgroud)