10-2. Propertyオブザーバ


Propertyオブザーバーは、Property値が変更されたかどうかを観察し、応答します.現在の値が新しい値に等しくても、値の設定時に呼び出されます.以下の場合、オブザーバを追加できます.
  • 定義された格納されたProperty
  • 保存されたProperty
  • を継承
  • 計算のプロパティ
  • を継承

    職業観察者

  • 職業観察者定義には、willsetとdidsetの2つの選択がある.
  • willsetは、値が保存される前に呼び出されます.定数パラメータは、新しいproperty値を渡します.willSet実装の一部として、このパラメータに新しい名前を付けることができます.パラメータ名と実装時に括弧を記入しない場合は、newValueの基本パラメータ
  • として作成されます.
  • didSetは、値が保存されると呼び出される.以前のproperty値を含む定数パラメータを渡します.パラメータ名またはoldValueをデフォルトパラメータとして使用できます.didSetオブザーバproperty値が割り当てられている場合は、先ほど設定した値の代わりに新しい値が使用されます.
  • スーパークラスpropertyのwill,didSetオブザーバーはスーパークラス初期化呼び出し後,サブクラスにpropertyを設定したときに呼び出す.スーパークラス初期化が呼び出されるまで、クラス自体のpropertyを設定しても呼び出されません.
  • class StepCounter {
    	var totalStep : Int = 0 {
        	willSet(newTotalSteps) {
            	print("\(newTotalSteps)")
            } didSet {
            	if totalSteps > oldValue {
                	print("\(totalSteps - oldValue)")
                }
            }
        }
    }
    
    let stepCounter = StepCounter()
    stepCounter.totalSteps = 200
    //윌셋에서 200 출력, 디드셋에서 200(200-0)출력한다.
    stepCounter.totalSteps = 360
    //윌셋에서 360, 디드셋에서 160(360-200)을 출력한다.
    //바뀌기 직전과 직후를 출력하므로 둘다 출력이 되고, 꼭 둘다 쌍으로 선언할 필요는 없다.
    //oldValue를 통해서 디드셋에서 바뀌기 전 값 접근이 가능하다는게 포인트다.
  • オブザーバを有するPropertyが入出力パラメータとして関数に渡されると、オブザーバは常に呼び出される.すなわち,関数に変化する変数の状態をリアルタイムで理解し,観察機能を実現できる.
  • 専門家

  • プロトコルは、プロトコル格納方式を管理するコードと、プロトコルを定義するコードとの間に分離層を追加する.wrappedValue propertyを定義する構造体、列挙型、またはクラスを作成し、property Rapperを定義します.
  • @propertyWrapper
    struct TwelveOrLess {
    	private var number = 0
        var wrappedValue : Int {
        	get {
            	return number
            }
            set {
            	number = min(newValue, 12)
            }
        }
        
        //게터는 저장된 값을 반환하고, 세터는 새로운 값이 12보다 작거나 같은걸 확인하고 반환한다. 
        //프로퍼티 래퍼는 wrappedValue를 프로퍼티의 값으로 반환해주게 된다.
    次の例では、Property Rapperを使用します.プロフェッショナルRapperはプロフェッショナルに一定の特性を与えるキーワードです.
    struct SmallRectangle {
    	@TwelveOrLess var height : Int
        @TwelveOrLess var width : Int
    }
    
    var rectangle = SmallRectangle()
    print(rectangle.height)
    //0을 출력한다.
    
    rectangle.height = 10
    //새로운 값을 넣어줬으니 setter가 작용할 것이다.
    print(rectangle.height)
    //아직 12보다 작으므로 10을 출력한다.
    
    rectangle.height = 24
    //세터가 작용하고 12보다 크므로 12를 출력한다.
  • パッケージのPropertyの初期値を設定するには、設定する前にProperty Rapperを直接初期化する必要があります.
  • @propertyWrapper
    struct SmallNumber {
    	private var maximum : Int
        private var number : Int
        
        var wrappedValue : Int {
        	get { return number }
            set { number = min(newValue, maximun) }
    //여기에서 보면 현재 구조체 내 프로퍼티에 값이 하나도 정의되지 않은 상태이다.   
    	init() {
        	maximum = 12
            number = 0
            
        init(wrappedValue : Int) {
        	maximun = 12
            number = min(wrappedValue, maximun)
        }
        
        init(wrappedValue : Int, maximum : Int) {
        	self.maximum = maximum
       	    number = min(wrappedValue, maximun)
        }
        //기본적으로 3개의 초기화를 지원한다. 
  • properの初期化タイプによって、初期化方式が異なります.次回確認できます.
  • struct ZeroRectangle {
    	@SmallNumber var height : Int
        @SmallNumber var width : Int
    }
    
    var zeroRectangle = ZeroRectangle()
    print(zeroRectangle.height, zeroRectangle.width)
    //0, 0을 출력한다. 가장 첫 init() 이 호출되기 때문이다.
    
    struct UnitRectangle {
    	@SmallNumber var height : Int = 1
        @SmallNumber var width : Int = 1
    }
    
    var unitRectangle = UnitRectngle()
    print(unitRectangle.height, unitRectangle.width)
    //1, 1을 출력한다. 초기값을 설정해줘도 init()을 호출한다. 
    //초기값으로 wrappedValue를 전달하지 않았기에 그렇다.
    
    struct NarrowRectangle {
    	@SmallNumber(wrappedValue : 2, maximun : 5) var height : Int
        @SmallNumber(wrappedValue : 3, maximun : 4) var width : Int
    }
    
    var narrowRectangle = NarrowRectangle()
    print(narrowRectangle.height, narrowRectangle.width)
    //2, 3을 출력한다. 다음에서는 래핑한 값을 통해서 초기화가 가능하다. 
    //위의 예시에서 3번에 해당하는 초기화를 사용하게 된다.
    //init(wrappedValue : Int, maximun : Int)