创建是下标(容易)一雨燕单数据模型,并分配到一个数组(硬) [英] Creating a Swift singleton data model that is subscripted (easy) and assignable to an array (hard)

查看:68
本文介绍了创建是下标(容易)一雨燕单数据模型,并分配到一个数组(硬)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在测试中,这将是方便的,其作用类似于阵列的简单的单数据模型。也就是说,如果我有一个单独的类名为MySingletonClass支持,你应该能够标数组中改变一个元素的sharedInstance变量:

In testing, it would be convenient to have a simple singleton data model that acts like an array. That is, if I have a singleton class called MySingletonClass that supports a sharedInstance variable you should be able to change an element in the array with subscripts:

MySingletonClass.sharedInstance[ 0 ] = "New item 0"

另外,该应用打开时,这将如一个简单的赋值允许系统提供到阵列初始值是巨大的:

Also, when the app opens, it would be great if a simple assignment allowed the system to provide initial values to the array:

MySingletonClass.sharedInstance = ["Item 0", "Item 1", "Item 2"]

我可以通过提供MySingletonClass用标/获取/设置语句做的第一。但是,我还没有发现有关如何执行后者的任何建议。这里是我的课,因为它目前存在的:

I can do the first by providing the MySingletonClass with a subscript/get/set statement. However, I've not found any suggestions about how to perform the latter. Here's my class as it currently exists:

class MySingletonClass {
    class var sharedInstance: MySingletonClass {
        get {
            struct Singleton {
                static let instance = MySingletonClass()
            }
            return Singleton.instance
        }
    }

    var toDoItems = [ String ]()

    subscript( row: Int ) -> NSString? {
        get {
            if isValidIndex( row ) {
                return toDoItems[ row ]
            } else {
                return nil
            }
        }
        set {
            assert(isValidIndex( row ), "invalid row number \(row) in a ToDo array of \(toDoItems.count) items")
            toDoItems[ row ] = newValue!
        }
    }
    /**
    Returns true if the passed index is valid in the toDoItems array

    The array is indexed from 0 to toDoItems.count - 1 (inclusive). The "row" value is judged
    to be valid only if it lies within this range.

    :param: row (Int), specifies an index number ( to count-1) in the toDoItems array
    :returns: a boolean to indicate if the passed index number is valid
    */
    func isValidIndex( row: Int ) -> Bool {
        if ( row >= 0 ) && ( row < toDoItems.count ) {
            return true
        } else {
            return false
        }
    }
}

有是使用数组分配给这个类初始值的kludgey方式:

There is a kludgey way to assign initial values to this class using an array:

MySingletonClass.sharedInstance.toDoItems = ["zero", "one", "two"]
println( "first element: '\(MySingletonClass.sharedInstance[ 0 ]!)'" )

然而,它迫使用户知道了toDoItems变量,我会preFER留隐患。有什么想法?

However, it forces the user to be aware of the "toDoItems" variable, which I would prefer to remain hidden. Any thoughts?

推荐答案

坏消息是,雨燕1.1不能使用标准的赋值运算符将一个数组赋值给一个单独的数据模型。苹果书斯威夫特编程语言还有一张纸条写着:

The bad news is that in Swift 1.1 you cannot use the standard assignment operator to assign an array to a singleton data model. In Apples book "The Swift Programming Language" there is a note saying:

这是不可能超载默认的赋值运算符(=)。

It is not possible to overload the default assignment operator (=).

当然,你可以把你自己的赋值操作符,但这只是离开你的用户在出发正常阵列实践猜测。您可能会看到一个单阵列可以使用结构中创建的建议。我一直没能作出这样的建议的工作作为一个真正的单身人士,看到这<一个href=\"http://stackoverflow.com/questions/27942237/swift-create-custom-class-of-type-array?rq=1\">link.

Of course, you could make up your own assignment operator, but that would just leave your users guessing at your departure from normal array practice. You may see suggestions that a singleton array could be created using a "struct". I have not been able to make that suggestion work as a true singleton, see this link.

好消息是,它是可能成立,否则行为就像一个数组一个单独的数据模型。也就是说,你可以使用标准的阵列功能,如.append,卸下摆臂:atIndex:等你只需要建立一个空文件斯威夫特并在下面的code粘贴。在code是两部分。第一个是一个称为项目协议。第二部分是建立单和封装所谓的项目一个数组的类。您可以添加,修改或提供的对象与项目的协议符合项目中删除对象。一个例子如下所示。

The good news is that it is possible to set up a singleton data model that otherwise acts just like an array. That is, you can use the standard array functions like ".append", ".remove: atIndex:", etc. You only have to set up an empty Swift file and paste in the code below. The code is in two parts. The first is a protocol called Item. The second part is a class that establishes the singleton and encapsulates an array called "items". You can add, modify, or remove objects in "items" provided that the objects conform with the Item protocol. An example is shown below.

Model.data.append( myItemConformingInstance )

提醒一句,X code有一定难度presenting单身,这使得文件X $ C $Ç真的颠簸。欢迎您来修改它,但你需要耐心!

One word of warning, Xcode has some difficulties presenting singletons and this file makes Xcode really twitchy. You're welcome to modify it, but you will need patience!

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
    }
}

要测试这一点,想象一下你想要的单阵列重新待办事项清单上present项目。你的类可能是这样的:

To test this, imagine you want the singleton array to represent items on a to-do list. Your class might look like:

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:

The singleton array Model.data is in your project just by including its class. You can begin adding to the singleton array Model.data by instantiating your Item class and appending:

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 )

在除了斯威夫特Array对象提供的标准功能,还有一个方法,描述(上限为:int?标题:字符串:)。该方法返回一个字符串,它列出在单阵列中的每个项目。极限参数是可选的,并告诉您只希望看到的项目数量有限,从你的阵列系统。标题参数使文本在说明的开始。例如:

In addition to the standard functions provided by Swift Array objects, there is a method, description( limit:Int?, title:String: ). The method returns a string listing each item in your singleton array. The limit parameter is optional, and tells the system that you only want to see a limited number of items from your array. The title parameter puts a text at the start of the description. For example:

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

这篇关于创建是下标(容易)一雨燕单数据模型,并分配到一个数组(硬)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆