ajax同步属性_创建一个同步的UserDefaults属性包装器

ajax同步属性

Since the birth of Swift’s property wrappers, I have seen numerous articles discussing how to create a wrapped variable that can persist itself to the UserDefaults sector. While many of the code examples suffice for the simplest case, I’ve still had some questions gnawing at the back of my head.

自Swift的属性包装器诞生以来,我已经看过许多文章,讨论如何创建一个包装的变量,该变量可以持久保存到UserDefaults部门。 尽管许多代码示例足以满足最简单的情况,但我仍然有些疑问仍然困扰着我。

  • If several of these instances would ever need to be alive at the same time, how will they synchronize?

    如果其中几个实例需要同时处于活动状态,它们将如何同步?
  • How do they synchronize if another process accesses the UserDefaults file, perhaps directly through the file system?

    如果另一个进程(可能直接通过文件系统)访问UserDefaults文件,它们如何同步?

In this article, we look at how to make a property wrapper that persists your variables in UserDefaults. They also stay on top of any value changes, regardless of where they originate.

在本文中,我们将研究如何制作一个属性包装器,以将变量保留在UserDefaults中。 无论它们来自何处,它们也始终处于任何价值变化之上。

分析一个简单的实现 (Analyzing a Simple Implementation)

Let’s take a look at a simple implementation of a property wrapper for persisting values in UserDefaults, to get a sense of what it does well and where it fails. We then use these realizations to build a wrapper that handles synchronization like a charm.

让我们看一下用于在UserDefaults中持久保存值的属性包装器的简单实现,以了解它的作用和失败之处。 然后,我们使用这些实现来构建一个包装,该包装像魅力一样处理同步。

The above is the most straightforward wrapper I’ve seen so far. It does not attempt to cache the currently persisted value; it merely relies on the caching mechanisms of UserDefaults. This solution is not terrible if that’s all you need. However, it becomes a headache as soon as you want to be able to reactively propagate changes out to, for example, a UI.

上面是到目前为止我所见过的最简单的包装器。 它不会尝试缓存当前持久化的值。 它仅依赖于UserDefaults的缓存机制。 如果这就是您所需要的,那么此解决方案并不可怕。 但是,一旦您希望能够以响应方式将更改传播到例如UI上,就会变得很头疼。

进行改进 (Making Our Improvements)

To be able to publish any changes made, we need to cache the value inside of our object. We can then listen to the notifications that UserDefault sends through NotificationCenter, and update our cached value whenever we receive one of those. This approach alleviates the problem of synchronization between multiple wrapper instances since each of them triggers a notification when they set a new value. It does, however, require us to switch from writing a struct to writing a class, since NotificationCenter can only register reference type objects.

为了能够发布所做的任何更改,我们需要将值缓存在对象内部。 然后,我们可以侦听UserDefault通过NotificationCenter发送的通知,并在收到其中之一时更新我们的缓存值。 这种方法减轻了多个包装器实例之间的同步问题,因为每个包装器实例在设置新值时都会触发通知。 但是,这确实需要我们从编写结构切换为编写类,因为NotificationCenter只能注册引用类型对象。

As we can see in the above implementation, we change a few things so that the object makes use of the cached value property. We also conform to the ObservableObject protocol and make use of Combine’s Published construct to let others react to any changes.

正如我们在上述实现中所看到的,我们做了一些更改,以便对象利用缓存的value属性。 我们还遵守ObservableObject协议,并利用Combine的Published结构让其他人对任何更改做出React。

The real improvement here is that the wrapper has a chance to react to any changes made through an instance of UserDefaults. Since all other wrapper objects go through UserDefaults, they are covered too.

真正的改进是包装器有机会对通过UserDefaults实例进行的任何更改做出React。 由于所有其他包装对象都通过UserDefaults,因此它们也被覆盖。

Does this mean that we are safe?Unfortunately not.

这是否意味着我们安全?

照顾意外地方的变化 (Taking Care of Changes From Unexpected Places)

What would happen if someone set a new value for our key from another process? Or updates the default values by writing straight to the property list file that holds them? With this implementation, we don’t have a way of handling that. It looks like we’ve spotted a potential improvement!

如果有人在另一个过程中为我们的密钥设置了新的值,将会发生什么? 还是直接写入保存默认值的属性列表文件来更新默认值? 使用此实现,我们没有一种处理方法。 看来我们已经发现了潜在的改进!

The UserDefaults property list is the least common denominator that may cause our wrappers or the UserDefaults instance to go out-of-sync. This file is a classic key-value list that can be read and written to, just like any other. To pick up on changes that originate from strange places, we need to monitor that list and react to any write operations that occur. To do that, we are going to engineer a FileWriteMonitor.

UserDefaults属性列表是最小公分母,它可能导致我们的包装器或UserDefaults实例不同步。 该文件是一个经典的键值列表,可以像其他任何文件一样被读取和写入。 要获取来自陌生地方的更改,我们需要监视该列表并对发生的所有写操作做出React。 为此,我们将设计一个FileWriteMonitor

There are probably many things going on in this piece of code that some may not have seen before. The FileWriteMonitor keeps a DispatchSourceFileSystemObject instance. That is a long word to describe an object that lets us observe events in a file descriptor.

在这段代码中可能发生了许多以前可能未曾看到的事情。 FileWriteMonitor保留一个DispatchSourceFileSystemObject实例。 这是一个很长的字眼,用来描述一个对象,它使我们可以观察文件描述符中的事件。

Speaking of which, our monitor also keeps track of a file descriptor. A file descriptor is a POSIX construct that allows us to get an abstract handle to access a file. It is this beauty, together with the DispatchSourceFileSystemObject, that is going to make magic for us.

说到这一点,我们的监视器还跟踪文件描述符。 文件描述符是POSIX构造,它使我们能够获取访问文件的抽象句柄。 正是这种美感与DispatchSourceFileSystemObject一起为我们带来了魔力。

Focus your attention on the .connectDispatchSource method. If we have obtained a file descriptor and created an event source before, we cancel and close them. These actions make sure we don’t leave a lot of loose ends hanging around.

将注意力集中在.connectDispatchSource方法上。 如果之前已经获得文件描述符并创建了事件源,则取消并关闭它们。 这些动作确保了我们不会留下很多松散的末端。

Once that is done, we obtain a new file descriptor for the file we are interested in. In our case, this is the UserDefaults property list file. From the descriptor, we create a new event source that will observe and propagate write and delete operations to us. In the event handler, we check for the event that triggered our handler. We reconnect our event source to a new file descriptor if we observe a delete event. If the event source passes us a write event, we call the onWrite closure that we got in the initializer.

一旦完成,我们将为感兴趣的文件获取一个新的文件描述符。在我们的例子中,这是UserDefaults属性列表文件。 从描述符中,我们创建一个新的事件源,它将观察并向我们传播写入和删除操作。 在事件处理程序中,我们检查触发了处理程序的事件。 如果观察到delete事件,我们会将事件源重新连接到新的文件描述符。 如果事件源向我们传递了一个写事件,我们将调用初始化程序中获得的onWrite闭包。

Now, let’s take this thing for a spin. Let’s see how we can use it to complete our property wrapper.

现在,让我们旋转一下。 让我们看看如何使用它来完成我们的属性包装器。

Pay special attention to the initializer in this piece of code. We first obtain a URL for the UserDefaults list, which we pass into our FileWriteMonitor together with a closure. That closure calls an update method which reads out the new value from the file and caches it. You may also notice that it passes the new value to UserDefaults, which allows it to update its cache and send a notification to the other property wrappers that may want to update their caches as well.

在这段代码中要特别注意初始化程序。 我们首先获取UserDefaults列表的URL,并将其与闭包一起传递到FileWriteMonitor 。 该闭包调用一个update方法,该方法从文件中读取新值并将其缓存。 您可能还会注意到,它将新值传递给UserDefaults,这使它可以更新其缓存并将通知发送给可能也要更新其缓存的其他属性包装器。

Image for post
Our final property wrapper at play!
我们最后的属性包装器正在发挥作用!

更多灵感 (More Inspiration)

In case you are interested to learn more, I post a fair share of articles. Feel free to follow me to get an update when there’s something new to read!

如果您有兴趣了解更多信息,我会发布很多文章。 有新内容要阅读时,请随时关注我以获取更新!

Until next time, you may be interested in reading about how to perform Monte Carlo simulations on a mobile device, or how to program in a protocol-oriented fashion. Below are links to those articles!

在下一次之前,您可能有兴趣阅读有关如何在移动设备上执行蒙特卡洛模拟或如何以面向协议的方式进行编程的信息。 以下是这些文章的链接!

Thanks for reading!

谢谢阅读!

翻译自: https://medium.com/swlh/creating-a-synchronized-userdefaults-property-wrapper-b2d9629b0aae

ajax同步属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值