类型删除容器是 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/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西: