【问题标题】:Is it possible to assign an array to a class property by reference rather than a copy?是否可以通过引用而不是副本将数组分配给类属性?
【发布时间】:2014-08-27 17:09:23
【问题描述】:

背景:

我设计了一个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

我想做的是将我的viewModel 中的一个数组的引用分配给我的tableSection.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)
            })
    }
}

viewModel 上的 refresh() 方法访问我的 API 服务,在响应时更新它的 collection 属性,并在 RACSignalnext 事件上提供结果(RACSignal 是一个提供的类Reactive 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 来实现相当于通过引用传递 Foundation NSArrayNSMutableArray

奖金问题

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

【问题讨论】:

  • 我添加了一个工作示例和更多细节。除非我遗漏了什么,否则我不相信 inout 参数是我的解决方案。
  • 嗯...这个标题可能会产生误导,你说在 Swift 中,你不能通过引用传递数组,如果提到传递给函数,就是不正确,因为它可以使用inout 完成。如果您的意思不同,最好至少更新一下标题。
  • 同意。欣赏“口头”表达担忧,而不仅仅是投反对票。
  • 新标题更有意义,现在很清楚你想要达到的目标:)

标签: swift class-design foundation


【解决方案1】:

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

所讨论的类可以是非常轻量级的——基本上,它只是作为一个瘦包装器,携带数组,客户端直接通过类实例访问数组——或者,恰恰相反,你可以设计管理数组的类,即该类特意提供了一个类似数组的 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 中一样,完全没有大惊小怪。

【讨论】:

  • 我也在 /r/swift 上发布了这个并且得到了类似的建议。我有点担心传递类可能引起的复杂性和混乱,并且可能会改变我的控制器的结构,将单个 tableSection 移动到 viewModel 或整个数据源。跨度>
  • “传递课程可能引起的复杂性和混乱”哦,fladdoodle。这些到底是什么“复杂性和混乱”,而不是通过引用传递数组?
  • 我只是坐在这里,在脑海中重构我的项目。这只是我从项目中看到的即将出现的代码气味。您的解决方案以简单有效的方式完全回答了我的问题,我可能遇到的任何类型的设计问题都在我身上。感谢您的帮助。
  • @HighFlyingFantasy 我添加了一些代码来向您展示这实际上是多么简单。我真的相信你想多了。
猜你喜欢
  • 2013-06-11
  • 2018-03-22
  • 2021-12-20
  • 2019-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-01
  • 2012-08-25
相关资源
最近更新 更多