EXC_BAD_ACCESS 使用泛型时

EXC_BAD_ACCESS when using generics

我正在尝试使用 Swift 中的泛型为我的实体实现缓存。这是我的代码:

class BaseCache<T>: NSObject {

    var allEntities = [T]()

    // MARK: - Append

    func appendEntities(newEntities: [T]) {
        for entity in newEntities {

            // Check if allEntities array already contains an entity
            var contains = false
            for item in allEntities {
                // EXC_BAD_ACCESS in isEqual method (see below)
                if isEqual(entity, rightEntity: item) {
                    contains = true
                    break
                }
            }

            if !contains {
                allEntities.append(entity)
            }
        }
    }

    func isEqual(leftEntity: T, rightEntity: T) -> Bool {
        return false
    }
}

下面是BaseCache的具体实现:

class CourierCache<T: AftershipCourier>: BaseCache<T> {

    override func isEqual(leftEntity: T, rightEntity: T) -> Bool {
        println("\(leftEntity)") // EXC_BAD_ACCESS here
        println("\(rightEntity)")
        return rightEntity.slug == leftEntity.slug
    }
}

有什么解决办法吗?谢谢!

PS:请注意this question与我的问题无关

您可以像这样修改您的代码来实现您所需要的。使用 Equatable 协议来比较 AfterCourier 实例。并使用类型别名来固定 CourierCache 中的类型。

class BaseCache<T:Equatable> {

    var allEntities :Array<T> = []

    func appendEntities(newEntities: [T])
    {
        for entity in newEntities {

            if !contains(allEntities,entity)
            {
                allEntities.append(entity)
            }
        }
    }

    func print()
    {
        println("Entities : \(allEntities)")
    }

}

class CourierCache<S>: BaseCache<AftershipCourier>
{
    func testCourier()
    {
        for courier in allEntities
        {
            println("Courier Slug: \(courier.slug)")
        }
    }
}


class AftershipCourier : Equatable,Printable
{
    var description: String {
        return "Courier Slug: \(slug)"
    }
    var slug : Int = 0
}

func == (lhs: AftershipCourier, rhs: AftershipCourier) -> Bool
{
    return lhs.slug == rhs.slug
}

typealias AfterCourierCache = CourierCache<AftershipCourier>

你可以这样使用它。

    var cache = CourierCache<AftershipCourier>()
    var t1 = AftershipCourier()
    t1.slug = 1
    var t2 = AftershipCourier()
    t2.slug = 2

    cache.appendEntities([t1])
    cache.appendEntities([t2])
    cache.print()
    cache.testCourier();

在我看来你发现了一个 Swift 错误。这是我能得到的最简单的方法:

class C { }

class Base<T> {

    func callCrash(t: T) {
        crash(t)
    }

    func crash(t: T) {  }
}

class Sub<T: C>: Base<T> {
    override func crash(t: T) {
        println(t) // EXC_BAD_ACCESS here
    }
}

let sub = Sub<C>()
sub.callCrash(C())

但是,将检测相等性的能力放入协议中,然后要求对象(而不是缓存)检查是否相等可能会更好。

@rakeshbs 的回答显示了如何使用 Equatable 执行此操作,但我要添加一些警告,这意味着您可能不想使用这种方法:

  1. 您正在检查 属性、slug 以测试是否相等。 Swift 中的相等意味着可替换性——即如果两个元素通过 == 相等,它们应该 完全等价 并且您应该能够用一个替换另一个而不用任何人注意到。如果您的飞船的属性可能会有所不同,即使它们的 slug 属性 相同,情况也不会如此。如果您使用 containssort 等依赖于这种可替换性 属性 的库函数,这可能会导致一些严重的错误。如果您正在使用 类,那么您可能会发现恒等运算符 (===) 是用于实现相等运算符的好东西。

  2. 使用 equatable 和 == 运算符和泛型意味着您的比较函数将静态绑定,因为运算符不是成员函数。这意味着如果您在缓存中保存层次结构中的不同对象,您将不会在 == 运算符上获得动态调度。也就是说,如果你有一个 AftershipCourier 缓存并在其中放入 FastAftershipCourier 类,你会发现 AftershipCourier== 是 运行 之间,而不是比较 FastAftershipCourier 的自定义 ==。因此,如果您需要动态行为,请确保 == 在传入的参数上调用一个方法,该方法可以被 sub类 覆盖,而不是直接比较属性。

要解决这两个问题,请使用您自己设计的带有比较功能的协议,让快递员 类 实施它,然后在您的缓存代码中调用它。

P.S。您的 for 循环根据 allEntities 检查实体可以写成 let alreadyContained = contains(allEntities) { entity.comparingFuncYouDecideOn([=27=]) }