更多内容欢迎关注公众号:Swift花园
Swift中的通用对象池
对象池模式一种创建设计模式。它的主要理念是创建一组对象,即一个对象的池子,然后从这个池子中请求和释放对象,以取代直接不断创建和释放这些对象的方式。👍
为什么要这么做呢?为了提升性能。举个例子,Dispatch 这个框架就采用了对象池模式,它为开发者提供了预先创建好的队列,因为创建队列本身需要比较昂贵的开销。
另外一个对象池模式的用例是 worker。举个例子,你需要从网络上下载数以千计的图片,但同一时间最多下载5个,那你就可以用5个worker对象来实现。相比为每个图片下载请求都创建一个 worker 来说,只创建少量 worker 要节省得多。🖼
那这个模式有没有什么弊端呢?当然有一些。举个例子,如果你用对象池来存放 worker,由于这些 worker 可能包含状态或者敏感的用户数据,你必须很小心地使用它们,比如当你试图重置 worker 以便提供给下一个使用者的时候。在多线程环境下,你还得留心线程安全问题。
这里有一个简单的线程安全的泛型对象池的例子:
import Foundation
class Pool<T> {
private let lockQueue = DispatchQueue(label: "pool.lock.queue")
private let semaphore: DispatchSemaphore
private var items = [T]()
init(_ items: [T]) {
self.semaphore = DispatchSemaphore(value: items.count)
self.items.reserveCapacity(items.count)
self.items.append(contentsOf: items)
}
func acquire() -> T? {
if self.semaphore.wait(timeout: .distantFuture) == .success, !self.items.isEmpty {
return self.lockQueue.sync {
return self.items.remove(at: 0)
}
}
return nil
}
func release(_ item: T) {
self.lockQueue.sync {
self.items.append(item)
self.semaphore.signal()
}
}
}
let pool = Pool<String>(["a", "b", "c"])
let a = pool.acquire()
print("\(a ?? "n/a") 被请求")
let b = pool.acquire()
print("\(b ?? "n/a") 被请求")
let c = pool.acquire()
print("\(c ?? "n/a") acquired")
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .seconds(2)) {
if let item = b {
pool.release(item)
}
}
print("对象池没有可用资源,阻塞线程。")
let x = pool.acquire()
print("\(x ?? "n/a") 被再次请求")如你所见,实现用了一些代码,你得到一个线程安全的泛型对象池。当池子里没有对象时,分发信号量将阻塞。整个池子依靠请求和释放两个方法工作。
在上面的例子中你可以看到,当池中没有对象可用时,队列将阻塞,直到有对象被释放为止。因此,使用对象池时,当心主线程被阻塞。😉
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~