测试已测试 | ✓ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布上次发布 | 2017年4月 |
SwiftSwift 版 | 3.0 |
SPM支持 SPM | ✓ |
由 Anton Bronnikov 维护。
Swift辅助框架,用于简化对象之间的关系。
目前支持的以下类型关系:
向下滚动以查看安装指南。
在两个对象之间建立弱一对一对关系的促进者是泛型类Weak1to1Relation
。
考虑以下示例
import Relations
class Tile : CustomStringConvertible {
private var address: UnsafeMutableRawPointer {
return Unmanaged.passUnretained(self).toOpaque()
}
var description: String {
return "tile.\(address)"
}
private (set) var _tileForComponent: Weak1to1Relation<Tile, Component>! = nil
var component: Component? {
get { return _tileForComponent.counterpart?.side }
set { _tileForComponent.relate(to: newValue?._componentForTile) }
}
init() {
_tileForComponent = Weak1to1Relation(side: self)
}
}
class Component : CustomStringConvertible {
private var address: UnsafeMutableRawPointer {
return Unmanaged.passUnretained(self).toOpaque()
}
var description: String {
return "component.\(address)"
}
private (set) var _componentForTile: Weak1to1Relation<Component, Tile>! = nil
var tile: Tile? {
get { return _componentForTile.counterpart?.side }
set { _componentForTile.relate(to: newValue?._tileForComponent) }
}
init() {
_componentForTile = Weak1to1Relation(side: self, { [unowned self] in
self.tileDidChange()
})
}
private func tileDidChange() {
print("[\(self)].[tile] -> [\(tile?.description ?? "nil")]")
}
}
现在,Component
和Tile
类型的对象可以通过同步更新两端的弱引用链接相互关联。
例如,以下代码不会导致断言失败:
let tile = Tile()
let component = Component()
tile.component = component
assert(component.tile === tile,
"The other side of the relation is automatically set")
此外,以下也不会失败:
let tile2 = Tile()
component.tile = tile2
assert(tile2.component === component,
"Yet again, the other side of relation is also set")
assert(tile.component == nil,
"And the object that was on the other side previously is updated")
更新也会在关系的一边被销毁时触发
var component2: Component! = Component()
component2.tile = tile
assert(tile.component === component2,
"Both sides of the relation are set")
component2 = nil // Dispose component2
assert(tile.component == nil,
"Consequently, the tile's side of the relation is set to nil")
可以注册闭包以接收关系更新的通知。
例如,请注意上述示例中这两种初始化之间的差异:
_tileForComponent = Weak1to1Relation(side: self)
vs.
_componentForTile = Weak1to1Relation(side: self, { [unowned self] in
self.tileDidChange()
})
后一种将自身方法注册为通知处理程序在关系每次更改时触发。此类通知将在关系更改的所有更改全部完成后运行,因此您永远不会陷在中间。
注意:请确保使用[unowned self]
捕获列表来避免不可避免的强引用循环。
泛型类Weak1toNRelation
和WeakNto1Relation
配对,用于促进弱1:N关系管理。
用法与1:1情况类似
class File : CustomStringConvertible {
let name: String
var description: String { return "\"\(name)\"" }
private (set) var _fileInFolder: WeakNto1Relation<File, Folder>! = nil
var folder: Folder? {
get { return _fileInFolder.counterpart?.side }
set { _fileInFolder.relate(to: newValue?._folderForFiles) }
}
init(name: String) {
self.name = name
_fileInFolder = WeakNto1Relation(side: self)
}
}
class Folder : CustomStringConvertible {
let name: String
var description: String { return "\"\(name)\"" }
private (set) var _folderForFiles: Weak1toNRelation<Folder, File>! = nil
var files: [File] {
return _folderForFiles.counterparts
.map({ $0.side })
.sorted(by: { $0.name < $1.name })
}
private func filesDidChange() {
print("Files in \(self)")
files.forEach({
print("- \($0)")
})
}
init(name: String) {
self.name = name
_folderForFiles = Weak1toNRelation(side: self, { [unowned self] in
self.filesDidChange()
})
}
func insert(file: File) {
_folderForFiles.relate(to: file._fileInFolder)
}
func insert(files: File...) {
_folderForFiles.relate(to: files.map({ $0._fileInFolder }))
}
func remove(file: File) {
_folderForFiles.unrelate(from: file._fileInFolder)
}
func remove(files: File...) {
_folderForFiles.unrelate(from: files.map({ $0._fileInFolder }))
}
}
…然后
let etc = Folder(name: "etc")
let hosts = File(name: "hosts")
let services = File(name: "services")
etc.insert(files: hosts, services)
IndexedWeak1toNRelation
和IndexedWeakNto1Relation
配对的辅助类促进了索引弱1:N关系的建立。
简而言之,这与上面提到的1:N关系几乎相同。区别在于,N侧的对象使用了索引值(参数类型泛型列表中的第3种类型)进行注册。这个值可以在关系的单值侧用来查找特定的多值对象。如果关系中新插入的多值对象与已有对象索引相同,将会有一个踢除行为。在这种情况下,之前的对象会被踢出。
为了快速说明,以下是一个Folder
/File
的例子,可以重写为:
class File {
let name: String
private (set) var _fileInFolder: IndexedWeakNto1Relation<File, Folder, String>! = nil
init(name: String) {
self.name = name
_fileInFolder = IndexedWeakNto1Relation(side: self, index: name)
}
}
class Folder {
private (set) var _folderForFiles: IndexedWeak1toNRelation<Folder, File, String>! = nil
var fileNames: [String] {
return _folderForFiles.indices.sorted()
}
init(name: String) {
_folderForFiles = IndexedWeak1toNRelation(side: self)
}
func getFile(withName name: String) -> File? {
return _folderForFiles.lookup(index: name)?.side
}
}
关于1:N关系的重要特性注意点是其通知,如果注册了的话,总是只触发一次,并且是在所有更改完成后。所以,例如,如果你将一批N侧对象从一个1侧对象转移到了另一个对象,那么每个对象只会收到一次通知。
将以下依赖项添加到你的Package.swift
.Package(url: "https://github.com/courteouselk/Relations.git", majorVersion: 0)