简易的自动回收池实现

想基于UIScrollView实现类似于UITableView的重用机制,发现可以单独把这一个简易的回收池抽象出来。

初始定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Pool<T: NSObject> {
private var elements : [T]
private var availableElems : [T]
private var _capacity = 0
var capacity : Int {
return self._capacity
}

init(capacity: Int, proc: ((T) -> Void)? = nil) {
self._capacity = capacity
self.elements = [T]()
for _ in 0..<capacity {
let elem = T()
proc?(elem)
self.elements.append(elem)
}
self.availableElems = self.elements
}

init(array: [T]){
self._capacity = array.count
self.elements = array
self.availableElems = self.elements
}

var availableCount : Int {
return self.availableElems.count
}

func allElements() -> [T] {
return self.elements
}

func removeAll(beforeRemove proc: ((T) -> Void)? = nil) {
if let process = proc {
for elem in self.elements {
process(elem)
}
}
self.elements.removeAll()
self.availableElems.removeAll()
self._capacity = 0
}
}

添加fetch(取出)和recycle(回收)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
func fetch() -> T? {
let elem = self.availableElems.isEmpty ? nil : Optional(self.availableElems.removeLast())
return elem
}

func recycle(elem: T) -> Bool {
assert(self.capacity != 0, "Pool capacity is 0")
if !self.elements.contains(elem) || self.availableElems.contains(elem) {
return false
}
self.availableElems.append(elem)
return true
}

遍历

这样一个基本的回收池就成型了,在此基础上,希望这个回收池能更加像一个容器,即像Array一样使用for…in遍历,为此,需要实现SequenceType协议:

1
2
3
4
public protocol SequenceType {
typealias Generator : GeneratorType
public func generate() -> Self.Generator
}

SequenceType协议可以看出我们需要实现一个GeneratorGenerator需要遵从GeneratorType协议:

1
2
3
4
public protocol GeneratorType {
typealias Element
public mutating func next() -> Self.Element?
}

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class PoolGenerator<T:NSObject> : GeneratorType {
typealias Element = T?
var index = 0
var maxElem : Int
var elements : [T]
init(array: [T]) {
self.elements = array
self.maxElem = array.count
}
func next() -> PoolGenerator.Element? {
return index >= maxElem ? nil : self.elements[index++]
}
}

PoolGenerator可用于顺序遍历一个数组,实现该Generator后,即可实现SequenceType:

1
2
3
4
5
6
7
8
9
10
class Pool<T: NSObject> : SequenceType {

typealias Generator = PoolGenerator<T>

// ......

func generate() -> Pool.Generator {
return PoolGenerator(array: elements)
}
}

自动回收

主动调用recycle()方法回收元素会带来很多不便,接下来实现一个通过初始定义的条件判断并自动回收的池:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class AutoRecyclingPool<T: NSObject> : Pool<T> {
// 用于判断是否可回收
private var criterion : ((T) -> Bool)?
// 已取出的元素集
private var fetchedElems : [T]

init(capacity: Int, canRecycle criterion: ((T) -> Bool)? = nil, proc: ((T) -> Void)? = nil) {
self.fetchedElems = [T]()
self.criterion = criterion
super.init(capacity: capacity, proc: proc)
}

override func fetch() -> T? {
var elem = super.fetch()
if elem == nil {
//开始回收
if let canRecycle = criterion {
for elem in self.fetchedElems {
if canRecycle(elem) {
print("Recycled: \(elem)")
self.availableElems.append(elem)
self.fetchedElems.removeObject(elem)
}
}
elem = super.fetch()
}
}
self.fetchedElems.append(elem!)
return elem
}

override func recycle(elem: T) -> Bool {
if super.recycle(elem) {
for i in 0..<self.fetchedElems.count {
if self.fetchedElems[i] == elem {
self.fetchedElems.removeAtIndex(i)
break
}
}
return true
}else {
return false
}
}

override func removeAll(beforeRemove proc: ((T) -> Void)?) {
super.removeAll()
self.fetchedElems.removeAll()
}
}

上述代码中对数组对象调用的removeObject方法,swift的数组类型本身没有实现,可通过extension添加

使用

有了AutoRecyclingPool,即可实现类似于UITableView的重用机制,criterion设置为判断view是否在其superview的显示区域之外,当然,还可以进一步扩充这个Pool的功能,当没有可回收的元素时,新创建一个加入池中,让fetch()方法始终能返回一个非空元素。