想基于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
协议可以看出我们需要实现一个Generator
,Generator
需要遵从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()
方法始终能返回一个非空元素。