Плохая новость заключается в том, что в Swift 1.1 вы не можете использовать стандартный оператор присваивания присвоить массив одноплодной модели данных. В книге «Яблоки» «Быстрый язык программирования» есть примечание:
Невозможно перегрузить оператор присваивания по умолчанию (=).
Конечно, вы могли бы создать свой собственный оператор присваивания, но это оставило бы у ваших пользователей возможность угадывать ваш уход из обычной практики массивов. Вы можете увидеть предложения о том, что одноэлементный массив можно создать с помощью «struct». Я не смог заставить это предложение работать как настоящий синглтон, см. Это link.
Хорошей новостью является то, что можно создать модель данных singleton, которая в противном случае действует как массив. То есть вы можете использовать стандартные функции массива, такие как «.append», «.remove: atIndex:» и т. Д. Вам нужно только установить пустой файл Swift и вставить код ниже. Код состоит из двух частей. Первый - это протокол под названием Item. Вторая часть - это класс, который устанавливает синглтон и инкапсулирует массив под названием «элементы». Вы можете добавлять, изменять или удалять объекты в «элементах» при условии, что объекты соответствуют протоколу Item. Пример показан ниже.
Model.data.append(myItemConformingInstance)
Одно слово предупреждения, Xcode имеют некоторые трудности, представляющие одиночка, и этот файл делает Xcode действительно раздражительным. Вы можете изменить его, но вам нужно терпение!
import Foundation
/**
This module provides a simple Data Model for you to use in a Model-View-Controller setup. The class Model provides a singleton called data. You can treat the "data" singleton as if it were an array (see below for an exception related to assignments). Model.data stores objects that conform to the Item protocol. You provide your own class that conforms to this protocol, and also provides instance variablesimport Foundation
*/
protocol Item {
/**
Returns a single-line description of the data stored in an instance conforming to the Item protocol.
Required to support plist-like description of the values stored in the items array
:returns: (String) a description of the item's data
*/
var description: String { get }
/* Make your Item-conforming class also conform to Equatable. It should have a function that determines if two instances of your class are considered equal. The function must be in the same file, but NOT INSIDE your Item-conforming class. The function looks like:
func ==(left: <NewItemClass>, right: <NewItemClass>) -> Bool
*/
}
// MARK: - start of Model singleton
/**
The Model.data singleton should look like a Swift Array, so it supports these functions:
ACCESSING ARRAY ELEMENTS
subscript(_: Int)
gets or sets existing elements using square bracket subscripting. If the index is not valid for the current items array then an assertion is triggered. It is not possible to insert additional items into the array using subscripts larger than the current highest index for the items array (see .append).
subscript(_: Range<Int>)
gets or sets a subrange of existing elements in the items array. If any of the indices in the specified range are not valid then an assertion is triggered. A subrange can be replaced (or eliminated) by a new array of objects conforming to the Item protocol, and the new array does not have to have the same number of entries as is specified by the Range.
ADDING OR REMOVING ELEMENTS
Model.data.append(newItem: Item)
Adds a new Item-conforming object as the last element in the items array
Model.data.insertAtIndex(<Item>, atIndex: <Int>)
Inserts an Item-conforming object into the items array at the given index. If the index is not valid then an assertion is triggered.
Model.data.removeAtIndex(<Int>)
Removes the element in the items collection at the given index and returns the removed element. If the index is invalid then an asertion is triggered
Model.data.removeLast()
Removes the last element in the items collection and returns the removed element. If the items array is already empty then an assertion is triggered.
Model.data.removeAll(keepCapacity: Bool)
Removes all elements from the items array and by default clears the underlying storage buffer
Model.data.reserveCapacity(minimumCapacity: Int)
QUERYING THE ARRAY OF ITEMS
Model.data.count
Returns the number of elements in the items array.
Model.data.isEmpty
Returns true if the items array is empty, otherwise returns false.
Model.data.capacity
Returns an integer value representing the number of elements that the items array can store without reallocation.
Model.data.isValidIndex(index: Int) (not standard)
returns true if the given index is between 0 and items.count - 1, otherwise it returns false.
Model.data.description(optional limit: <Int>?) (not standard)
returns "empty" if the items array is empty, otherwise returns an enumerated list of Item descriptions. Ordinarily all items are listed, unless you provide a limit to tell the system how many items to list.
ISSUES
Although it would be nice to be able to assign Model.data usign the standard approach, e.g.
*Model.data = [ item1, item2, item3 ]
*THIS WON'T WORK, and the Swift 1.1 documentation says that the assign operator (=) cannot be overridden. Instead, use the underlying items array. E.G:
*Model.data.items = [ Item1, Item2, Item3 ]
This approach uses the normal singleton method for Swift 1.1. Reportedly, it gets much simpler in Swift 1.2
*/
class Model {
class var data: Model {
get {
struct Singleton {
static let instance = Model()
}
return Singleton.instance
}
}
var items = [ Item ]()
// MARK: - ACCESSING ARRAY ELEMENTS
subscript(index: Int) -> Item? {
get {
if isValidIndex(index) {
return items[ index ]
} else {
return nil
}
}
set {
assert(isValidIndex(index),
"Fatal error: could not replace an item in Model.data using index number: \(index) in an items array of: \(items.count) items")
items[ index ] = newValue!
}
}
subscript(subRange: Range<Int>) -> Slice<Item> {
get {
assert(isValidIndex(subRange.startIndex),
"Fatal error: could not retrieve a range of items in Model.data using low index number: \(subRange.startIndex) from an items array of: \(items.count) items")
// in testing, it looks as though subRange is always set up to do a test of range
// [ .startIndex..<.endIndex ], which means that .endIndex can be equal to the count
// of elements in a array.
assert(subRange.endIndex <= items.count,
"Fatal error: could not retrieve a range of items in Model.data using high index number: \(subRange.endIndex) from an items array of: \(items.count) items")
return self.items[ subRange ]
}
set {
assert(isValidIndex(subRange.startIndex),
"Fatal error: could not replace a range of items in Model.data using low index number: \(subRange.startIndex) in an items array of: \(items.count) items")
assert(subRange.endIndex <= items.count,
"Fatal error: could not replace a range of items in Model.data using high index number: \(subRange.endIndex) in an items array of: \(items.count) items")
items[ subRange ] = newValue
}
}
// MARK: - ADDING OR REMOVING ELEMENTS
/**
Adds a new object conforming to Item at the end of items array
:param: newItem (Item) an object conforming to the Item protocol
*/
func append(newItem: Item) {
self.items.append(newItem)
}
/**
Inserts a new item into the items array based on a passed index number
The insertion places an object conforming with the Item protocol into the items array at the position specified by atIndex. Existing items that are at that position or higher are moved up by 1. Insertions fail if the passed index number is not valid.
:param: newItem (Item) an object conforming with Item
:param: atIndex (Int) the position in the list where the new task name is to be inserted.
*/
func insert(newItem: Item, atIndex: Int) {
assert(isValidIndex(atIndex),
"Fatal error: could not insert a new item into Model.data with invalid index number: \(atIndex) in an items array of: \(items.count) items")
self.items.insert(newItem, atIndex: atIndex)
}
/**
removes an item at the items array at the specified index position
If the specified index is not valid then a runtime error is generated. After a successful deletion, the items that started with index numbers higher than the passed index number will end with their index numbers decremented by 1.
:param: atIndex (Int) the index number of the data item to be removed
:returns: (Item) This is the item that was removed from the items array.
*/
func removeAtIndex(atIndex: Int) -> Item? {
assert(isValidIndex(atIndex),
"Fatal error: could not remove an item from Model.data using index number: \(atIndex) in an items array of: \(items.count) items")
return self.items.removeAtIndex(atIndex)
}
/**
removes the last Item object in the items array in Model.data
If the items array is empty then a runtime error is generated
:returns: (Item) an object conforming to the Item protocol that was removed from the items array.
*/
func removeLast() -> Item {
assert(self.items.count > 0, "Fatal error: could not remove the 'last' item from Model.data since the items collection was already empty.")
return self.items.removeLast()
}
/**
Removes all items in the items array
:param: keepCapacity: Bool if true then overrides default clearing of the underlying buffer
*/
func removeAll(keepCapacity: Bool = false) {
self.items.removeAll(keepCapacity: keepCapacity)
}
/*
Ensures that the underlying storage for the items array can hold the given total number of elements
*/
func reserveCapacity(minimumCapacity: Int) {
self.items.reserveCapacity(minimumCapacity)
}
// MARK: - QUERYING THE ARRAY OF ITEMS
/**
Returns an integer value indicating how many items the items array in Model.data can store
You will have to reallocate the Model.data.items array if storage capacity is exceeded, using
reserveCapacity.
:returns: (Int) the number of items that the items array can store.
*/
var capacity: Int {
get {
return self.items.capacity
}
}
/**
Returns the number of items in the items array
*/
var count: Int {
get {
return items.count
}
}
/**
Returns true if the items array of Model.data is empty
:returns: (Bool) true if the items array s empty, otherwise false.
*/
var isEmpty: Bool {
get {
return self.items.isEmpty
}
}
/**
Returns true if the passed index is valid in the data array
The items array is indexed from 0 to items.count - 1 (inclusive). The index is judged to be valid only if it lies within this range.
:param: index (Int), specifies an index number in for an object in the items array.
:returns: a boolean to indicate if the passed index number is valid
*/
func isValidIndex(index: Int) -> Bool {
if (index >= 0) && (index < items.count) {
return true
} else {
return false
}
}
/**
Returns a string in the form of an ennumerated list that shows a description for each item in the items array
:param: limit: Int? tells the system to limit the list to "limit" items.
:returns: (String) the ennumerated list of items
*/
func description(limit: Int? = nil, title: String? = nil) -> String {
var msg = ""
if let label = title {
msg = "*** \(label)\n"
}
if items.count == 0 {
msg += "the items array is empty\n"
} else {
var index = 0
for nextItem in self.items {
msg = msg + "[\(index)].\t\(nextItem.description)\n"
index++
}
}
return msg
}
}
Чтобы проверить это, представьте, что вы хотите, чтобы одиночный массив представлял элементы в списке дел. Ваш класс может выглядеть так:
import Foundation
class ToDoItem: Item, Equatable {
var name: String
var createdAt: NSDate
var done: Bool
init(name: String, createdAt: NSDate, done: Bool = false) {
self.name = name
self.createdAt = createdAt
self.done = done
}
var description: String {
get {
return "Task Name:\(name), Created On:\(createdAt), Done: \(done)"
}
}
}
// External function to make equivalence between Items testable
func ==(left: ToDoItem, right: ToDoItem) -> Bool {
return
left.done == right.done
&&
left.createdAt == right.createdAt
&&
left.name == right.name
}
Одноэлементный массив Model.data находится в вашем проекте, просто включив его класс. Вы можете начать добавлять к одноплодной массив Model.data по инстанцированию своего класса Item и добавления:
let task1 = ToDoItem(name: "task 1", createdAt: NSDate(), done: false)
let task2 = ToDoItem(name: "task 2", createdAt: NSDate(), done: false)
let task3 = ToDoItem(name: "task 3", createdAt: NSDate(), done: false)
Model.data.append(task1)
Model.data.append(task2)
Model.data.append(task3)
В дополнении к стандартным функциям, предоставляемых Swift объектов Array, есть метод, description(limit:Int?, title:String:)
. Метод возвращает строку, перечисляющую каждый элемент в вашем одноэлементном массиве. Предельный параметр является необязательным и сообщает системе, что вы хотите видеть только ограниченное количество элементов из вашего массива. Параметр title помещает текст в начале описания. Например:
Model.data.description(title: "First Items")
Это приводит следующую строку:
> *** First Items:
>[0]. Task Name:task 1, Created On:2015-03-05 22:25:14, Done: false
>[1]. Task Name:task 2, Created On:2015-03-05 22:25:14, Done: false
>[2]. Task Name:task 3, Created On:2015-03-05 22:25:14, Done: false
Хаудите Blacksquare, – user2121934
Спасибо за ваш любезный ответ. Это кажется неблагодарным, но есть ли способ сделать sharedInstance похожим на массив? Если бы он проиндексировал его, было бы здорово, если бы я мог дать ему назначение, «.delete []» или «.insert afterIndex:». -George – user2121934
Вы могли бы предоставить несколько примеров, чтобы я мог точно видеть, о чем вы говорите? – kellanburket