概要
関数(呼び出し元)毎に一度だけ処理を実行させるInuというライブラリを書いてみました。
背景
iOSアプリ開発していると一度だけ処理を実行させたい場面がたまにあります。
例えば、viewWillAppear(animated:)
等でCustomViewをaddSubView
させたい場合などあったりするのではないでしょうか。
この場合だとBool型のpropertyでFlag制御したり、dispatch_once()
を使った制御等が考えられます。
var isOnce: Bool = false
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if !isOnce {
anyFunction()
isOnce = true
}
}
var token: dispatch_once_t = 0
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
dispatch_once(&token) {
self.anyFunction()
}
}
実現したい内容やタイミングが多様化するとプロパティを増やしたりする必要がありそうです。
個人的には狭いスコープで使用するためのプロパティを増やすのはあまり好きではないです。
インスタンスメソッド単位での処理の制御なら簡単に出来そうだと思ってInuを書きました。
使い方
class ViewController: UIViewController, OnceType {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
once.call() {
anyFunction()
}
}
OnceTypeというprotocolを適用して
once.call() { }
を呼び出すことでクロージャの中の処理を一度だけ実行します。
中身は
public protocol OnceType: AnyObject { }
public extension OnceType {
public var once: Once {
guard let object = objc_getAssociatedObject(self, &OnceAssociatedObjectHandle.OnceKey) as? Once else {
let once = Once()
objc_setAssociatedObject(self, &OnceAssociatedObjectHandle.OnceKey, once, .OBJC_ASSOCIATION_RETAIN)
return once
}
return object
}
}
private var callStack: [String] = []
public func call(withKey: String = __FUNCTION__, @noescape _ closure: Void -> Void) {
if callStack.contains(withKey) {
return
}
closure()
callStack.append(withKey)
}
こんな感じです。
AnyObjectであればなんでもいいので、頻繁に使用するようなクラスがあれば
extension UIView: OnceType { }
extension UIViewController: OnceType { }
ベースクラスに書いてしまえば、継承しているサブクラスでも使えるのは利点かなと思ってます。
副作用もありそうですが。。。
他にも少し違った用途のメソッドも用意されています。
このわんこのことを可愛いと感じたらスターください。
\(^o^)/