是否可以通过引用而不是副本将数组分配给类属性?

时间:2014-08-27 17:09:23

标签: swift class-design foundation

背景

我设计了一个TableViewDataSource类,为UITableViewDataSourceUITableViewDelegate提供了一个实现。您实例化TableViewSection个对象,这些对象将传递给TableViewDataSource,用于配置单元格,节标题,句柄选择,行插入等。

TableViewSection对象有一个名为dataSource: [AnyObject]?的属性,设置后用于计算节中的行数,并为单元配置块提供一个对象:

// get the section, dequeue a cell for that section, retrieve the item from the dataSource
// ...
tableSection.cellConfigurationBlock?(cell: AnyObject, item: AnyObject?, indexPath: NSIndexPath)
return cell

我想要做的是从我的viewModeltableSection.dataSource分配对数组的引用,让我的viewModel更新数组,然后更新表格视图。在Swift中,您不能通过引用传递数组。解决方法似乎是使用NSMutableArray,但随之而来的是类型安全性的损失,以及从Swift到Foundation来回转换对象时的更大认知负担。

工作示例:

let kCellIdentifier = "SomeCellIdentifier"
class MyViewController: UITableViewController {
    // Property declarations
    @IBOutlet var tableDataSource: TableViewDataSource!

    var viewModel: MyViewControllerViewModel = MyViewControllerViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.setupTableView()
        self.refresh()
    }

    func setupTableView() {
        var tableSection = TableViewSection(cellIdentifier: kCellIdentifier)
        tableSection.dataSource = self.viewModel.collection
        // tableSection configuration
        // ...

        self.tableDataSource.addSection(tableSection)
    }

    func refresh() {
        self.viewModel
            .refresh()
            .subscribeNext({ result in
                self.tableView.reloadData()
            }, error: { error in
                self.logger.error(error.localizedDescription)
            })
    }
}

refresh()上的viewModel方法点击了我的API服务,在响应时更新了它的collection属性,并提供了next RACSignal事件的结果(RACSignalReactive Cocoa提供的一个类,除了这一点之外真的是这样。)

我找到了一种解决方法,包括每次进行单次更新时或批量更新后重新分配数据源。

func refresh() {
    self.viewModel
        .refresh()
        .subscribeNext({ result in
            self.updateDataSource()
            self.tableView.reloadData()
        }, error: { error in
            self.logger.error(error.localizedDescription)
        })
}

func updateDataSource() {
    self.tableDataSource.tableSectionForIndex(0)?.dataSource = viewModel.collection
}

此方法有效,但仅作为解决方法暂时使用。随着TableViewDataSource的增长和变得越来越复杂,这种方法在命令式过程代码中变得越来越复杂,这与我在编写类时要实现的目标相反。

问题

是否有任何解决方法可以坚持使用原生的Swift Array来实现相当于通过引用传递基础NSArrayNSMutableArray

奖金问题

有人可以为我提供一些类/结构设计技巧,以实现纯Swift中的预期目标吗?

1 个答案:

答案 0 :(得分:5)

简单的解决方案是将数组包装在一个类中。类实例通过引用传递,因此问题得到了有效解决:通过对类实例的任何引用对数组的更改会影响数组,如通过每个引用那样看到的类实例。

有问题的类可以非常轻量级 - 基本上,它只是作为一个包装数组的瘦包装器,客户端通过类实例直接访问数组 - 或者恰恰相反,你可以设计管理数组的类,即类故意提供类似于数组的API,以保护客户端免受底层实现的影响。这两种方法都可能是合适的;我当然都做到了。

这是第一种情况的例子。我的模型对象是属于UIDocument子类的数组。我的视图控制器是一个UITableViewController。用户将在表中查看,添加和编辑模型实体。因此,UITableViewController需要访问UIDocument的数组(恰好称为people)。

  • 在Objective-C中,我的UITableViewController只是对数组self.people的引用,它是一个NSMutableArray。这只是一个指针,因此self.people的更改也是对UIDocument people的更改 - 它们是同一个对象。

  • 在Swift中,我的UITableViewController包含对UIDocument对象self.doc的引用。这个数组现在是一个Swift数组,内部是"内部"它,所以我可以称之为self.doc.people。但是,重写太多了!相反,我创建了一个计算变量属性self.people,它充当self.doc.people的网关:

    var doc : PeopleDocument!
    var people : [Person] { // front end for the document's model object
        get {
            return self.doc.people
        }
        set (val) {
            self.doc.people = val
        }
    }
    
    哎呀,问题解决了。每当我说self.people.append(newPerson)之类的内容时,我都会直接传递给UIDocument的模型对象people,而我实际上是在追加它。因此,代码的外观和工作方式与Objective-C中的相同,完全不用担心。