[Swift设计模式] 对象池

1,006 阅读2分钟
更多内容欢迎关注公众号: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及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~