使用泛型时EXC_BAD_ACCESS

时间:2015-01-04 06:15:01

标签: ios generics swift exc-bad-access

我试图在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与我的问题无关

2 个答案:

答案 0 :(得分:1)

在我看来你发现了一个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可以在它们之间运行,而不是自定义比较==的{​​{1}}。因此,如果您需要动态行为,请确保FastAftershipCourier调用传入参数的方法,该方法可以被子类覆盖,而不是直接比较属性。

  3. 要解决这两个问题,请使用您自己设计的协议和比较函数,让courier类实现它,然后在缓存代码中调用它。

    P.S。你的for循环检查实体对所有实体可以写成==

答案 1 :(得分:0)

您可以像这样修改代码以实现您的需求。使用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();