[iOS] Swiftで簡単なプロパティ監視

2014/10/1

こんにちは。きんくまです。

Swiftでプロパティの変更をどうやったら他のオブジェクトに伝えられるか調べてました。

例えばこんな感じのライブラリもあるみたいです。

>> slazyk/Observable-Swift

あとは、NSObjectのサブクラスにして、KVOするやつ(Apple公式のObjective-Cの移行ガイドみたいな)

>> Key-Value Observing

だけど、swiftにはプロパティの変更をするときにwillSet, didSetというものを呼び出す仕組みがすでにあります。

>> Property Observers

だからこいつを使ってあげれば、簡単にできるのではないかと。

で、1つプロトコルを作ってみました

protocol PropertyChangeProtocol: class{
    func willSetProperty(name:String, sender:AnyObject, newValue:AnyObject!, oldValue:AnyObject!)
    func didSetProperty(name:String, sender:AnyObject, value:AnyObject!)
}

willSetPropertyの方では
name: 変更のあったプロパティ名
sender: 出したインスタンスの参照
newValue: 新しい値
oldValue: 古い値

didSetPropertyの法では
name: 変更のあったプロパティ名
sender: 出したインスタンスの参照
value: 新しい値

がわかります。これでとりあえずの情報としては足りるかなーと。
プロトコル名のうしろに :classというのがくっついているのですが、使い方のところで弱参照のweakキーワードを使いたいからです。ないとエラーになっちゃいました。

>> How can I make a weak protocol reference in ‘pure’ Swift (w/o @objc)

こいつを使ってdelegateパターンでやってみると、

import UIKit

protocol PropertyChangeProtocol: class{
    func willSetProperty(name:String, sender:AnyObject, newValue:AnyObject!, oldValue:AnyObject!)
    func didSetProperty(name:String, sender:AnyObject, value:AnyObject!)
}

class Person{
    weak var delegate:PropertyChangeProtocol!
    var age:Int = 25{
        willSet{
            delegate?.willSetProperty("age", sender: self, newValue:newValue, oldValue: self.age)
        }
        didSet{
            delegate?.didSetProperty("age", sender:self, value:self.age)
        }
    }
}

class Client:PropertyChangeProtocol{
    var taro:Person!
    
    init(){
        taro = Person()
        println("taro's age is \(taro.age)")
        taro.delegate = self
        taro.age = 12
    }
    
    func willSetProperty(name:String, sender:AnyObject,newValue: AnyObject!, oldValue: AnyObject!) {
        println("will change => name: \(name), sender: \(sender), new: \(newValue), old: \(oldValue)")
    }
    
    func didSetProperty(name:String, sender:AnyObject, value: AnyObject!) {
        println("did change => name: \(name), sender: \(sender), value: \(value)")
        if taro === sender {
            println("wow! sender is equal to taro")
        }
    }
}

let c = Client()

出力

taro's age is 25
will change => name: age, sender: __lldb_expr_236.Person, new: 12, old: 25
did change => name: age, sender: __lldb_expr_236.Person, value: 12
wow! sender is equal to taro

という感じです。

課題

protocolでメソッドを全部必須にしないでoptionalにしたいときは、@objcというキーワードが必要。
-> でもそれって純粋なswiftじゃないし、、。

>> Optional Protocol Requirements

複数の監視オブジェクトがもし必要になった場合に、delegateパターンだと実装が毎回面倒くさそう
-> delegateを配列にして、ループで全部のdelegateのprotocolメソッドを呼び出すっていうのはちょっと大変。1つのプロパティならいいけど、いくつもプロパティがあった場合に。小さなライブラリを作る?

LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

LINEスタンプ作りました!
毎日使える。とぼけたウサギ。LINEスタンプ販売中! 毎日使える。とぼけたウサギ

ページトップへ戻る