- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
类型删除容器是 Swift 中的有用结构,因为它目前无法支持传递泛型类型参数。社区对此有一些很好的解释:
这是一个例子:
protocol View: class {
associatedtype ViewModel: Equatable
var viewModel: ViewModel! { get set }
func render(_ viewModel: ViewModel)
}
class _AnyViewBoxBase<T: Equatable>: View {
var viewModel: T!
func render(_ viewModel: T) {
fatalError()
}
}
final class _ViewBox<Base: View>: _AnyViewBoxBase<Base.ViewModel> {
var base: Base!
override var viewModel: Base.ViewModel! {
get {
return base.viewModel
}
set {
base.viewModel = newValue
}
}
init(_ base: Base) {
self.base = base
}
override func render(_ viewModel: Base.ViewModel) {
base.render(viewModel)
}
}
final class AnyView<T: Equatable>: View {
var _box: _AnyViewBoxBase<T>
var viewModel: T! {
get {
return _box.viewModel
}
set {
_box.viewModel = newValue
}
}
func render(_ viewModel: T) {
_box.render(viewModel)
}
init<Base: View>(_ base: Base) where Base.ViewModel == T {
_box = _ViewBox(base)
}
}
struct ExampleViewModel {
let content: String
}
extension ExampleViewModel: Equatable {
static func ==(lhs: ExampleViewModel, rhs: ExampleViewModel) -> Bool {
return lhs.content == rhs.content
}
}
final class Example: View {
var viewModel: ExampleViewModel!
init(viewModel: ExampleViewModel) {
self.viewModel = viewModel
}
func render(_ viewModel: ExampleViewModel) {
}
}
这些类型删除盒允许我们构建通用容器或创建必须符合具有特定类型的通用协议(protocol)但不限于具体实现的属性。例如使用 AnyView
下面我可以轻松地交换一个 View 测试替身。
struct TypeUnderTest {
var view: AnyView<ExampleViewModel>
}
var example = Example(viewModel: ExampleViewModel(content: "hello"))
var instanceUnderTest = TypeUnderTest(view: AnyView(example))
到目前为止一切顺利。我可以类似地定义 View
有一个可选的或非可选的(而不是隐式解包可选)viewModel
属性并相应地按框更新。
weak var view: AnyView<ExampleViewModel>
不好。这将使我只剩下对盒子类型的弱引用,并且它会立即被释放。
var view: WeakAnyView<ExampleViewModel>
让我们更接近。我们可以创建一个弱引用其内容的框。如果我们的View
协议(protocol)只定义可选属性然后我们就可以开始了:
protocol View: class {
associatedtype ViewModel: Equatable
var viewModel: ViewModel? { get set }
func render(_ viewModel: ViewModel)
}
class _AnyViewBoxBase<T: Equatable>: View {
var viewModel: T?
func render(_ viewModel: T) {
fatalError()
}
}
final class _ViewBox<Base: View>: _AnyViewBoxBase<Base.ViewModel> {
weak var base: Base?
override var viewModel: Base.ViewModel? {
get {
return base?.viewModel
}
set {
base?.viewModel = newValue
}
}
init(_ base: Base) {
self.base = base
}
override func render(_ viewModel: Base.ViewModel) {
base?.render(viewModel)
}
}
final class AnyView<T: Equatable>: View {
var _box: _AnyViewBoxBase<T>
var viewModel: T? {
get {
return _box.viewModel
}
set {
_box.viewModel = newValue
}
}
func render(_ viewModel: T) {
_box.render(viewModel)
}
init<Base: View>(_ base: Base) where Base.ViewModel == T {
_box = _ViewBox(base)
}
}
struct ExampleViewModel {
let content: String
}
extension ExampleViewModel: Equatable {
static func ==(lhs: ExampleViewModel, rhs: ExampleViewModel) -> Bool {
return lhs.content == rhs.content
}
}
final class Example: View {
var viewModel: ExampleViewModel?
init(viewModel: ExampleViewModel?) {
self.viewModel = viewModel
}
func render(_ viewModel: ExampleViewModel) {
}
}
struct TypeUnderTest {
var view: AnyView<ExampleViewModel>
}
let viewModel = ExampleViewModel(content: "hello")
var example: Example? = Example(viewModel: viewModel)
let instanceUnderTest = TypeUnderTest(view: AnyView(example!))
instanceUnderTest.view.viewModel
example = nil
instanceUnderTest.view.viewModel
但是,如果我删除的协议(protocol) (View
) 定义了非可选属性,那么我们就会遇到问题。 _ViewBox
必须定义一个非可选的 viewModel
符合 View
但这迫使我们忽略了一个非常现实的可能性,即我们的弱引用装箱类型将被释放,并且我们没有安全的方式将其传达给调用者。
一个选择是添加另一个框,但这使用起来会变得很痛苦:
protocol View: class {
associatedtype ViewModel: Equatable
var viewModel: ViewModel { get set }
func render(_ viewModel: ViewModel)
}
class _AnyViewBoxBase<T: Equatable>: View {
var viewModel: T
func render(_ viewModel: T) {
fatalError()
}
init(viewModel: T) {
self.viewModel = viewModel
}
var empty: Bool {
get {
return false
}
}
}
final class _ViewBox<Base: View>: _AnyViewBoxBase<Base.ViewModel> {
weak var base: Base?
override var viewModel: Base.ViewModel {
get {
return base!.viewModel
}
set {
base?.viewModel = newValue
}
}
init(_ base: Base) {
super.init(viewModel: base.viewModel)
self.base = base
}
override func render(_ viewModel: Base.ViewModel) {
base?.render(viewModel)
}
override var empty: Bool {
get {
return base == nil
}
}
}
final class AnyView<T: Equatable>: View {
var _box: _AnyViewBoxBase<T>
var viewModel: T {
get {
return _box.viewModel
}
set {
_box.viewModel = newValue
}
}
func render(_ viewModel: T) {
_box.render(viewModel)
}
init<Base: View>(_ base: Base) where Base.ViewModel == T {
_box = _ViewBox(base)
}
var empty: Bool {
return _box.empty
}
}
struct AnyViewOptionalBox<T: Equatable> {
private var _view: AnyView<T>?
var view: AnyView<T>? {
get {
if let view = self._view, view.empty == false {
return view
} else {
return nil
}
}
set {
self._view = newValue
}
}
init(view: AnyView<T>) {
self.view = view
}
}
struct ExampleViewModel {
let content: String
}
extension ExampleViewModel: Equatable {
static func ==(lhs: ExampleViewModel, rhs: ExampleViewModel) -> Bool {
return lhs.content == rhs.content
}
}
final class Example: View {
var viewModel: ExampleViewModel
init(viewModel: ExampleViewModel) {
self.viewModel = viewModel
}
func render(_ viewModel: ExampleViewModel) {
}
}
struct TypeUnderTest {
var viewBox: AnyViewOptionalBox<ExampleViewModel>
}
let viewModel = ExampleViewModel(content: "hello")
var example: Example? = Example(viewModel: viewModel)
let anyView: AnyView<ExampleViewModel> = AnyView(example!)
let anyViewOptional: AnyViewOptionalBox<ExampleViewModel> = AnyViewOptionalBox(view: anyView)
let instanceUnderTest = TypeUnderTest(viewBox: anyViewOptional)
instanceUnderTest.viewBox.view?.viewModel.content
example = nil
instanceUnderTest.viewBox.view?.viewModel.content
有没有更好的方法来维护对类型删除属性的弱引用?
最佳答案
基本上您想要的是将类型删除框的生命周期链接到它包含的对象的生命周期,这样一旦包含的对象被释放,盒子就会被释放。
一种方法是确保盒子仅弱引用包含的对象,并使用 objc_setAssociatedObject(...) 使盒子成为包含对象的关联对象。这样,您基本上颠倒了两个对象之间的所有权关系。
请参阅下面的 playground 示例:
import ObjectiveC
protocol View: class {
associatedtype ViewModel: Equatable
var viewModel: ViewModel { get set }
func render()
}
private var AssociatedObjectHandle: UInt8 = 0
final class AnyView<T: Equatable>: View {
let _viewModelGetter: () -> T
let _viewModelSetter: (T) -> Void
let _render: () -> Void
init<Base: View>(_ base: Base) where Base.ViewModel == T {
//Ensure this object doesn't reference base, so there is no retain cycle
_viewModelGetter = { [weak base] in
//You can force unwrap, because it is guaranteed that base is not deallocated because of the association
return base!.viewModel
}
_viewModelSetter = { [weak base] in
//You can force unwrap, because it is guaranteed that base is not deallocated because of the association
base!.viewModel = $0
}
_render = { [weak base] in
//You can force unwrap, because it is guaranteed that base is not deallocated because of the association
base!.render()
}
//Associate this object with the base, so it gets deallocated when base gets deallocated, also base is guaranteed to exist during our lifetime
objc_setAssociatedObject(base, &AssociatedObjectHandle, self, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
deinit {
print("dealloc: \(self)")
}
var viewModel: T {
get {
return _viewModelGetter()
}
set {
_viewModelSetter(newValue)
}
}
func render() {
_render()
}
}
class ConcreteView: View {
typealias ViewModel = String
var viewModel: String
init(viewModel: String) {
self.viewModel = viewModel
}
deinit {
print("dealloc: \(self)")
}
func render() {
print("viewModel: \(viewModel)")
}
}
weak var anyView: AnyView<String>?
autoreleasepool {
var concreteView = ConcreteView(viewModel: "Test")
autoreleasepool {
anyView = AnyView(concreteView)
//Any view should render correctly because concrete view exists
anyView!.render()
}
//Success: anyView is not nil yet, because concreteView still exists
anyView!.render()
}
//Crash: anyView is now nil
anyView!.render()
输出:
viewModel: Test
viewModel: Test
dealloc: __lldb_expr_34.ConcreteView
dealloc: __lldb_expr_34.AnyView<Swift.String>
Fatal error: Unexpectedly found nil while unwrapping an Optional value
关于swift - 我们可以在 Swift 中创建具有非可选属性的类型删除弱引用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47724857/
我正在尝试编写一个相当多态的库。我遇到了一种更容易表现出来却很难说出来的情况。它看起来有点像这样: {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE
谁能解释一下这个表达式是如何工作的? type = type || 'any'; 这是否意味着如果类型未定义则使用“任意”? 最佳答案 如果 type 为“falsy”(即 false,或 undef
我有一个界面,在IAnimal.fs中, namespace Kingdom type IAnimal = abstract member Eat : Food -> unit 以及另一个成功
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
在 C# 中,default(Nullable) 之间有区别吗? (或 default(long?) )和 default(long) ? Long只是一个例子,它可以是任何其他struct类型。 最
假设我有一个案例类: case class Foo(num: Int, str: String, bool: Boolean) 现在我还有一个简单的包装器: sealed trait Wrapper[
这个问题在这里已经有了答案: Create C# delegate type with ref parameter at runtime (1 个回答) 关闭 2 年前。 为了即时创建委托(dele
我正在尝试获取图像的 dct。一开始我遇到了错误 The function/feature is not implemented (Odd-size DCT's are not implemented
我正在尝试使用 AFNetworking 的 AFPropertyListRequestOperation,但是当我尝试下载它时,出现错误 预期的内容类型{( “应用程序/x-plist” )}, 得
我在下面收到错误。我知道这段代码的意思,但我不知道界面应该是什么样子: Element implicitly has an 'any' type because index expression is
我尝试将 SignalType 从 ReactiveCocoa 扩展为自定义 ErrorType,代码如下所示 enum MyError: ErrorType { // .. cases }
我无法在任何其他问题中找到答案。假设我有一个抽象父类(super class) Abstract0,它有两个子类 Concrete1 和 Concrete1。我希望能够在 Abstract0 中定义类
我想知道为什么这个索引没有用在 RANGE 类型中,而是用在 INDEX 中: 索引: CREATE INDEX myindex ON orders(order_date); 查询: EXPLAIN
我正在使用 RxJava,现在我尝试通过提供 lambda 来订阅可观察对象: observableProvider.stringForKey(CURRENT_DELETED_ID) .sub
我已经尝试了几乎所有解决问题的方法,其中包括。为 提供类型使用app.use(express.static('public'))还有更多,但我似乎无法为此找到解决方案。 index.js : imp
以下哪个 CSS 选择器更快? input[type="submit"] { /* styles */ } 或 [type="submit"] { /* styles */ } 只是好
我不知道这个设置有什么问题,我在 IDEA 中获得了所有注释(@Controller、@Repository、@Service),它在行号左侧显示 bean,然后转到该 bean。 这是错误: 14-
我听从了建议 registering java function as a callback in C function并且可以使用“简单”类型(例如整数和字符串)进行回调,例如: jstring j
有一些 java 类,加载到 Oracle 数据库(版本 11g)和 pl/sql 函数包装器: create or replace function getDataFromJava( in_uLis
我已经从 David Walsh 的 css 动画回调中获取代码并将其修改为 TypeScript。但是,我收到一个错误,我不知道为什么: interface IBrowserPrefix { [
我是一名优秀的程序员,十分优秀!