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
执行此操作,但我要添加一些警告,这意味着您可能不想使用这种方法:
您正在检查 属性、slug
以测试是否相等。 Swift 中的相等意味着可替换性——即如果两个元素通过 ==
相等,它们应该 完全等价 并且您应该能够用一个替换另一个而不用任何人注意到。如果您的飞船的属性可能会有所不同,即使它们的 slug
属性 相同,情况也不会如此。如果您使用 contains
或 sort
等依赖于这种可替换性 属性 的库函数,这可能会导致一些严重的错误。如果您正在使用 类,那么您可能会发现恒等运算符 (===
) 是用于实现相等运算符的好东西。
使用 equatable 和 ==
运算符和泛型意味着您的比较函数将静态绑定,因为运算符不是成员函数。这意味着如果您在缓存中保存层次结构中的不同对象,您将不会在 ==
运算符上获得动态调度。也就是说,如果你有一个 AftershipCourier
缓存并在其中放入 FastAftershipCourier
类,你会发现 AftershipCourier
的 ==
是 运行 之间,而不是比较 FastAftershipCourier
的自定义 ==
。因此,如果您需要动态行为,请确保 ==
在传入的参数上调用一个方法,该方法可以被 sub类 覆盖,而不是直接比较属性。
要解决这两个问题,请使用您自己设计的带有比较功能的协议,让快递员 类 实施它,然后在您的缓存代码中调用它。
P.S。您的 for 循环根据 allEntities 检查实体可以写成 let alreadyContained = contains(allEntities) { entity.comparingFuncYouDecideOn([=27=]) }
我正在尝试使用 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
执行此操作,但我要添加一些警告,这意味着您可能不想使用这种方法:
您正在检查 属性、
slug
以测试是否相等。 Swift 中的相等意味着可替换性——即如果两个元素通过==
相等,它们应该 完全等价 并且您应该能够用一个替换另一个而不用任何人注意到。如果您的飞船的属性可能会有所不同,即使它们的slug
属性 相同,情况也不会如此。如果您使用contains
或sort
等依赖于这种可替换性 属性 的库函数,这可能会导致一些严重的错误。如果您正在使用 类,那么您可能会发现恒等运算符 (===
) 是用于实现相等运算符的好东西。使用 equatable 和
==
运算符和泛型意味着您的比较函数将静态绑定,因为运算符不是成员函数。这意味着如果您在缓存中保存层次结构中的不同对象,您将不会在==
运算符上获得动态调度。也就是说,如果你有一个AftershipCourier
缓存并在其中放入FastAftershipCourier
类,你会发现AftershipCourier
的==
是 运行 之间,而不是比较FastAftershipCourier
的自定义==
。因此,如果您需要动态行为,请确保==
在传入的参数上调用一个方法,该方法可以被 sub类 覆盖,而不是直接比较属性。
要解决这两个问题,请使用您自己设计的带有比较功能的协议,让快递员 类 实施它,然后在您的缓存代码中调用它。
P.S。您的 for 循环根据 allEntities 检查实体可以写成 let alreadyContained = contains(allEntities) { entity.comparingFuncYouDecideOn([=27=]) }