api集合_通过弱集合快速改善观察者模式api

api集合

Even if you don’t know what the Observer pattern is, there’s a good chance that you’ve applied it in the past. This design pattern is used to create APIs that notify one or more subjects about changes in a certain object, with the NotificationCenter being the most popular use of this pattern in iOS.

即使您不知道观察者模式是什么,您也很有可能在过去应用了它。 此设计模式用于创建API,这些API通知一个或多个主题某个对象的更改,而NotificationCenter是iOS中此模式的最常用用法。

One simple way to reproduce what the NotificationCenter does is to create a dictionary that maps a string (a notification) to an array of closures. Whenever that notification is "posted," all of the closures are executed:

重现NotificationCenter所做操作的一种简单方法是创建一个字典,该字典将字符串(通知)映射到闭包数组。 每当“发布”该通知时,将执行所有关闭:

However, the point of this article is not to attempt to reproduce the NotificationCenter but to show you what this implementation implies. You must be aware that whenever you're using the basic Swift dictionaries, arrays, or sets, all keys and values are retained! Additionally, you need to be aware that closures are reference types, so they are retained as well and can outlive their owners.

但是,本文的重点不是尝试重现NotificationCenter而是向您展示此实现的含义。 您必须意识到,无论何时使用基本的Swift字典,数组或集合,所有键和值都将保留! 此外,您需要注意,闭包是引用类型,因此它们也会保留下来,并且可能会超出其所有者的寿命。

So while this implementation works, it’s going to be a huge memory issue. Because it’s retaining the closures, they will never be unregistered. The notifications will attempt to execute them even if the object that registered it is long gone.

因此,尽管此实现有效,但它将成为一个巨大的内存问题。 因为它保留了关闭,所以它们永远不会被注销。 通知将尝试执行它们,即使注册它的对象早已消失。

If you’ve been working with iOS for a long time, you might remember that iOS’s own NotificationCenter had this issue! Prior to iOS 9, every observer had to be unregistered when being deallocated because if you didn't, it would attempt to execute it when it shouldn't and crash your app.

如果您使用iOS已有很长时间,您可能还记得iOS自己的NotificationCenter遇到了这个问题! 在iOS 9之前的版本中,每个观察者在被释放时都必须注销,因为如果您没有这样做,则它将在不应该执行的情况下尝试执行该应用,从而使您的应用崩溃。

deinit {
NotificationCenter.default.removeObserver(self, ...)
}

For our implementation, we could replicate this by adding the concept of “owners” to our closures so that we are able to remove them if one of them wishes to be unregistered. Fortunately, not only do we not need to go this far, but it’s also good if we don’t. If you’re developing an API, its usability should be one of your main priorities.

对于我们的实现,我们可以通过在闭包中添加“所有者”的概念来复制这一点,以便在其中一个希望取消注册的情况下将其删除。 幸运的是,我们不仅不需要走这么远,而且如果我们不需要这样做也很好。 如果您正在开发API,则其可用性应该是您的主要优先考虑之一。

In this case, let’s take a look at how we can create an observer API that is memory-safe while also not having to manually unregister the observers. The NotificationCenter problem shown above was fixed in iOS 9 (removing observers became an automatic process) when Apple started applying the same concept.

在这种情况下,让我们看一下如何创建内存安全的观察者API,同时又不必手动注销观察者。 当苹果开始应用相同的概念时,上面显示的NotificationCenter问题已在iOS 9中修复(删除观察者成为自动过程)。

弱集合 (Weak Collections)

Let’s pretend we have a deep link-based navigation system where “feature providers” can provide a feature (represented as a UIViewController) if they recognize the deep link that the app wants to present:

假设我们有一个基于深层链接的导航系统,其中“功能提供者”可以在识别出应用程序要呈现的深层链接时提供其功能(表示为UIViewController ):

Like in the NotificationCenter example, this suffers from a memory issue. If whatever provided those closures ceases to exist, the FeaturePusher class will still be able to execute the closure and potentially crash the app. Fortunately, there are a few useful types in Foundation that can assist us in improving that.

像在NotificationCenter示例中一样,这会遇到内存问题。 如果提供了那些闭包的东西不再存在, FeaturePusher类仍将能够执行闭包并可能使应用程序崩溃。 幸运的是, Foundation中有一些有用的类型可以帮助我们进行改进。

As I’ve shown before in my Weak Dictionary Values article, Foundation offers a series of lower-level Obj-C collection types that are more powerful than the basic Swift ones. Two of them specifically are NSMapTable and NSHashTable, which are Obj-C versions of Dictionary and Set, respectively. Both of them allow a higher range of memory management options, which include weak references for both values and keys. If instead of using a base Swift array, we used a NSMapTable that has our closures as values and weak references to whatever provided that block as a key, our navigation system would automatically evict and deallocate the closures whenever the related providers are deallocated. That's because if the weak component is deallocated in weak collections, the entire entry will be evicted from the collection.

正如我之前在弱字典值文章中所展示的那样, Foundation提供了一系列低级的Obj-C集合类型,它们比基本的Swift集合类型更强大。 其中两个是NSMapTableNSHashTable ,分别是DictionarySet Obj-C版本。 它们都允许使用更大范围的内存管理选项,其中包括对值和键的弱引用。 如果不是使用基本的Swift数组,而是使用NSMapTable ,它以闭包作为值,并且对提供该块的键的弱引用作为键,那么只要相关提供者被释放,我们的导航系统就会自动退出并释放闭包。 这是因为如果将弱组件分配到弱集合中,则整个条目将从集合中逐出。

Creating weak collections is just a matter of using the correct initializer. A dictionary with weak keys can be initialized with NSMapTable.weakToStrongObjects(), while one with weak values can be initialized with NSMapTable.strongToWeakObjects(). If we want our navigation system's closures to be automatically unregistered if the object that registered them was deallocated, we can create a weak-keyed dictionary that maps an object to an array of closures:

创建弱集合只是使用正确的初始化程序的问题。 以弱键的字典可以被初始化NSMapTable.weakToStrongObjects()而一个具有弱的值可以与被初始化NSMapTable.strongToWeakObjects() 如果我们希望导航系统的闭包在释放它们的对象被释放后自动取消注册,我们可以创建一个弱键字典,将一个对象映射到闭包数组:

lazy var providers = NSMapTable<AnyObject, NSHashTable<FeatureProviderBox>>.weakToStrongObjects()

Because the keys are weak, the closures will automatically be evicted from the dictionary if the key ceases to exist.

因为键很弱,所以如果键不存在,则会自动从字典中逐出闭包。

Note that NSMapTable is an Obj-C API, so all keys and values must be class objects. That's why we have to use a NSHashTable as a value instead of a regular Set or Array.

请注意, NSMapTable是Obj-C API,因此所有键和值都必须是类对象。 这就是为什么我们必须使用NSHashTable作为值而不是常规SetArray

You can make Obj-C types like NSMapTable capable of holding Swift structs by creating a generic Box class wrapper type. Here, we create one to be able to represent our feature closure as a class object (FeatureProviderBox) in order to be able to store it inside the NSHashTable.

您可以通过创建通用的Box类包装器类型,使Obj-C类型(如NSMapTable能够容纳Swift结构。 在这里,我们创建一个能够将功能闭包表示为类对象( FeatureProviderBox ),以便能够将其存储在NSHashTable

单元测试弱集合(和参考周期) (Unit Testing Weak Collections (and Reference Cycles))

To check if our improvement worked, we can create a unit test that checks if the correct view controllers are returned:

为了检查我们的改进是否有效,我们可以创建一个单元测试来检查是否返回了正确的视图控制器:

However, we are mostly interested in seeing if the automatic eviction is working. To test that the observers are being deallocated and the closures are being evicted, we can use an autoreleasepool. As described in my autoreleasepool article, you can use a pool whenever you want something to be deallocated as soon as possible:

但是,我们最感兴趣的是查看自动驱逐是否有效。 要测试观察者是否已被释放,并且关闭了关闭,我们可以使用autoreleasepool 。 如我的autoreleasepool文章中所述 ,您可以在希望尽快重新分配某些东西时使用一个池:

You’ll see that this test will pass, but if you’re not sure why, try removing the pool to see what happens. The test will fail, and the reason is that objects aren’t deallocated as soon as they go out of scope in iOS (that will usually happen at the end of a RunLoop).

您会看到此测试将通过,但是如果不确定原因,请尝试删除池以查看会发生什么。 测试将失败,原因是对象不在iOS范围内时不会立即释放(通常会在RunLoop的末尾发生)。

In this case, the pool is simply a way to force it to deallocate immediately for unit testing purposes. This same trick can be applied to unit-test any type of reference cycle situation.

在这种情况下,池只是强制其为单元测试目的立即取消分配的一种方法。 此技巧可用于对任何类型的参考循环情况进行单元测试。

结论 (Conclusion)

Weak collections are a great way to build better APIs, but you must be aware of their possible limitations. While types like NSHashTable and NSPointerArray are all-around great tools, you may see that NSMapTable's documentation tells you to be careful with weakToStrongObjects(). In that configuration, although the values are ejected from the table as expected, they still may be held in memory for a larger period of time. That's why this article didn't attempt to fully reproduce the NotificationCenter, as it took me a while to realize that doing so would require a pretty ugly workaround.

弱集合是构建更好的API的好方法,但是您必须意识到它们的可能限制。 虽然像NSHashTableNSPointerArray这样的类型是全方位的好工具,但您可能会看到NSMapTable的文档告诉您使用weakToStrongObjects()要小心。 在这种配置中,尽管值按预期从表中弹出,但它们仍可以在内存中保留更长的时间。 这就是为什么本文没有尝试完全重现NotificationCenter ,因为花了我一段时间才意识到这样做将需要一个非常丑陋的解决方法。

However, you'll find that NSHashTable is good to go under any configuration.

但是,您会发现NSHashTable在任何配置下都很好。

翻译自: https://medium.com/better-programming/improve-observer-pattern-apis-in-swift-with-weak-collections-4b6271420c90

api集合

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值