在Swift中创建一个NSMutabaleArray子类

7
我可以帮您翻译成中文:

我正在尝试创建一个支持数组字面量初始化的NSMutableArray子类,使用Swift语言。

我可以创建一个简单的子类:

class MyArray: NSMutableArray {
}

初始化工作正常:

let arrayInstance: MyArray = MyArray()

然而,如果我尝试使用数组字面量:
let arrayInstance: MyArray = ["one"]

它在运行时失败,[NSMutableArray initWithCapacity:]方法仅为抽象类定义。

我可以添加此初始化器,但是作为结果,我还必须添加其他所需的初始化器:

class MyArray: NSMutableArray {
  override init(capacity numItems: Int) {
    super.init(capacity: numItems)
  }

  required init(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
  }

  required convenience init(arrayLiteral elements: AnyObject...) {
      fatalError("init(arrayLiteral:) has not been implemented")
  }
}

很不幸,第二个必需的初始化器会产生以下编译错误:

扩展声明目前无法被覆盖

看起来我卡住了!有人能提供一种创建支持数组字面量的 NSMutableArray 子类的方法吗?否则我要回到 Objective-C!


1
为什么你需要一个数组子类?为什么不使用Swift数组? - Teejay
2
你说得对,由于NSMutableArray的实现方式(你想要的init来自于NSArrayCreation类别,而不是类本身),这可能无法在Swift中完成。如果你是其他人,我会讲一堂关于不要子类化类簇除非你深刻了解它们的内容(即使你知道了也不要这样做,因为这很疯狂并且总是会带来麻烦),但在你的情况下,我认为你知道这一点,所以我只是好奇为什么你要子类化NSMutableArray - Rob Napier
2
@RobNapier,这是非常好的通用建议!我子类化NSMutableArray的原因是我想要一个可观察的数组(就像观察者模式中的那样)。我之前在Objective-C中使用过它,以便更轻松地将动态数据绑定到表视图上(http://www.scottlogic.com/blog/2014/11/04/mutable-array-binding-reactivecocoa.html)。不幸的是,我一直遇到障碍!不能子类化Array,因为它是一个结构体,创建自己的结构体会导致ObjC互操作问题(它没有自动桥接),因此选择了第三个选项!第四个选项是在ObjC中编写此代码;-) - ColinE
4
我以前也建过类似的东西,但是我使用了HAS-A而不是IS-A。它具有与数组相同的基本接口,并在放入和删除事物时添加/删除观察结果。这与NSArrayController的思路相同(它也是HAS-A而不是IS-A)。避免了类簇的所有麻烦。 - Rob Napier
@ColinE 这可能是一次碰运气,但你不能在Array的适当属性(比如count)中添加一个属性观察器吗?我认为你可以在Array的扩展中实现这个功能,不是吗? - wltrup
1个回答

0
这里的问题不在于数组字面量,而是第一次初始化没有正常工作,如果你这样做也会遇到“相同”的崩溃:
let arrayInstance: MyArray = MyArray()
arrayInstance.count

//OR
let secondArrayInstance: MyArray = MyArray(objects: ["one"], count: 1) // method used by literals syntax

问题在于子类化NSMutableArray,因为它是一个类簇。根据苹果公司的说法,几乎没有理由这样做:
“通常很少有理由子类化NSArray。该类能够很好地完成其设计任务——维护对象的有序集合。但是,在某些情况下,自定义的NSArray对象可能会派上用场。以下是一些可能性:
- 更改NSArray存储其集合元素的方式。您可能出于性能原因或更好地与旧代码兼容而这样做。 - 获取有关集合正在发生的情况的更多信息(例如,统计信息收集)。”
类簇定义
“类簇”是Foundation框架广泛使用的一种设计模式。类簇将许多私有具体子类分组到一个公共抽象超类下面。以这种方式对类进行分组可以简化面向对象框架的公开可见体系结构,而不会降低其功能丰富性。类簇基于抽象工厂设计模式。
NSArray的替代方案
在创建自定义的 NSArray 类之前,应该先研究一下 NSPointerArray 和相应的 Core Foundation 类型 CFArray Reference。因为 NSArray 和 CFArray 是“无桥转换”的,你可以在代码中使用 CFArray 对象替换 NSArray 对象(适当进行类型转换)。虽然它们是相应的类型,但 CFArray 和 NSArray 没有完全相同的接口或实现,有时你可以使用 CFArray 完成一些难以使用 NSArray 实现的操作。例如,CFArray 提供了一组回调函数,其中一些用于实现自定义保留-释放行为。如果你将这些回调函数指定为 NULL 实现,就可以轻松地获得一个非保留数组。
如果你想要添加的行为是作为现有类的补充,那么可以编写一个 NSArray 的分类。但请记住,这个分类会对你使用的所有 NSArray 实例生效,这可能会产生意想不到的后果。或者,你可以使用组合来实现所需的行为。
子类化 NSArray
为了子类化 NSArray,我们必须重写 count 和 objectAtIndex: 方法。
任何 NSArray 的子类都必须重写原始实例方法 count 和 objectAtIndex:。这些方法必须操作您为集合元素提供的后备存储。
概念上,数组存储若干个数据项,每个数据项都可以通过索引访问。NSArray 通过其两种原语方法 count 和 objectAtIndex: 表达了这个抽象概念。借助这些方法作为基础,其他方法(派生方法)可以被实现;
静态数组示例
class Months: NSArray {

    let months =  [ "January", "February", "March", "April",
                    "May", "June", "July", "August", "September",
                    "October", "November", "December" ]

    override var count: Int {
        get {
          return months.count
        }
    }

    override func objectAtIndex(index: Int) -> AnyObject {
        assert(index < count, "The index is out of bounds")
        return months[index]
    }
}

初始化

let months = Months()

println("My birthday month : \(months.objectAtIndex(6))")
println("The last month of the year : \(months.lastObject)")

带有对象的数组

class MyArray: NSArray {

    var array: [AnyObject] = []

    func populateWith(#objects: [AnyObject]) {
        self.array = objects
    }

    override var count: Int {
        get {
          return array.count
        }
    }

    override func objectAtIndex(index: Int) -> AnyObject {

        assert(index < count, "The index is out of bounds")
        return array[index]
    }
}

初始化

let fruits = MyArray()
fruits.populateWith(objects: ["Apple", "Banana", "Orange", "Pear", "Cherry"])

println("Fruits count : \(fruits.count)")

子类化NSMutableArray

它与NSArray相同,但我们必须重写这些方法:

  • count
  • objectAtIndex:
  • insertObject:atIndex:
  • removeObjectAtIndex:
  • addObject:
  • removeLastObject
  • replaceObjectAtIndex:withObject:

如果需要,我可以添加示例。

结论

我们可以在Swift中对NSArray进行子类化(仅在使用默认初始化程序init()时),但我们无法(尚未)使用其他初始化程序(例如initWithObjects:count:)。

因此,我同意您的观点,目前最好的解决方案是在Objective-C中对类簇进行子类化。

类簇文档

NSArray文档

希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接