Skip to content

Instantly share code, notes, and snippets.

@okstring

okstring/KVO.md Secret

Last active August 10, 2021 13:09
Show Gist options
  • Save okstring/e2fab9d2a2a82e529888728c040ccd5e to your computer and use it in GitHub Desktop.
Save okstring/e2fab9d2a2a82e529888728c040ccd5e to your computer and use it in GitHub Desktop.

KVO

동작방식

  1. 감지하려는 객체는 NSObject를 상속하고 해당 프로퍼티에 @objc dynamic 을 붙여줘야 한다

    class Address: NSObject {
        @objc dynamic var town: String
        init(town: String) {
            self.town = town
        }
    }
  2. 옵저버를 등록한다 대상은 KeyPath 사용

    var observers = [NSKeyValueObservation]()
    let address = Address(town: "히말라야")
    let observation = address.observe(\.town, options: [.old, .new]) { (object, change) in
        print(change.oldValue ?? "", change.newValue ?? "")
    }
    observers.append(observation)
  3. 옵션에 따라 observe 에서 선언한 대로 동작한다

    address.town = "두덕리"
    //Optional("히말라야") Optional("두덕리")

option

  • .old: 변경 이전 값, oldKey로 확인

  • .new: 새로 변경된 값, newKey로 확인

  • .initial: initial은 초기화시에도 이 observer handler를 호출할거냐...라고 보면 될 것 같습니다.

    •   let address = Address(town: "히말라야")
        let observation = address.observe(\.town, options: [.old, .new, .initial]) { (object, change) in
            print(change.oldValue ?? "nil", change.newValue ?? "nil", object.town, change.isPrior)
        }
        address.town = "두덕리"
        
        //nil 히말라야 히말라야 false
        //히말라야 두덕리 두덕리 false
  • .prior: 변경이 일어날 때, 변경 전후로 알림을 별도로 보냄.

    •   let observation = address.observe(\.town, options: [.old, .new, .prior]) { (object, change) in
            print(change.oldValue ?? "nil", change.newValue ?? "nil", object.town, change.isPrior)
        }
        address.town = "두덕리"
        //히말라야 nil 히말라야 true
        //히말라야 두덕리 두덕리 false

옵저버 제거

iOS9 이전에는 객체가 해제되기 전에 옵저버를 제거하지 않으면 메모리 릭이나 크래시 등이 발생하였음, iOS9 이후에는 시스템이 자동으로 제거해주기 때문에 직접 제거하지 않아도 됨.

옵저빙을 그만하고 싶다면 제거를 해야겠습니다.

프로퍼티 옵저버와 모양이 비슷?

프로퍼티 옵저버는 타입 정의 내부에 위치해야 하는 반면, KVO는 타입 정의 외부에서 obsever를 추가할 때 사용하는 모양

해당 타입 속성들도 따로 observe해야 한다. object라면 얘기가 다름

장단점

장점

  • 객체의 구현을 변경하지 않고 내부 객체의 상태 변화에 대응할 수 있음. (SDK객체의 경우)

    • 예를 들어 라이브러리에 들어있는 프로퍼티의 변경사항을 알고싶다!
    • 그런데 NSObject를 상속받고 있지 않고 @objc dynamic도 붙어있지 않다면 장점이라 볼 수 있는걸까?
  • 관찰된 프로퍼티의 이전값(oldValue)와 최신값(newValue)를 제공

  • KeyPath를 사용하여 프로퍼티를 관찰하므로 nested 프로퍼티도 관찰 가능

    •   class Address: NSObject {
            @objc dynamic var town: String
        
            init(town: String) {
                self.town = town
            }
        }
        class Person: NSObject {
            @objc dynamic var address: Address
        
            init(address: Address) {
                self.address = address
            }
        }

단점

  • NSOject 상속, dynamic을 붙여야 하는 조건
  • 많은 value를 감지할 때는 많은 조건문이 필요.
  • 프로퍼티 옵저버와 역할이 많이 겹친다.
  • UIKit과는 잘 어울리지 않는다 참고

Static dispatch

dynamic dispatch

KVC(Key-Value Coding)

객체의 값을 직접 가져오지않고, Key 또는 KeyPath 를 이용해서 간접적으로 데이터를 가져오거나 수정하는 방법

dynamic

dynamic이 사용되는 이유는 바로 Swift와 Objective-C와의 상호운용성Interoperability)

Swift와 Objective-C가 같이 잘 작동한다고 해도, Objective-C의 모든 기능을 Swift에서 사용할 수 있는 것은 아니기 때문

dynamic 선언된 멤버는 Objective-C 런타임을 사용하여 항상 동적으로 디스패치 되며

해당 멤버는 컴파일러에 의해 인라인되거나 가상화 되지 않습니다.

쉽게 말을 하면 해당 멤버를 Objective-C 코드나 클래스로 속성이나 메소드에 사용 될 수 있습니다.

dynamic 키워드는 Objective-C 기본 클래스에 Swift 클래스를 하위클래스로 만들 때 사용합니다.

reference

https://zeddios.tistory.com/1220

ios-study-boost/iOSInterviewquestions#7

https://dongkyprogramming.tistory.com/9


Swift Design Patterns 2018 by Hacking with Swift 중

대안: key-value observing(이상한 예)

변경 사항을 모니터링 할 수있는 다른 방법이 있지만, 너무 추하고 너무 비정상적이어서 여기에 포함하는 것에 대해 두 가지 생각을했습니다. 그러나 때로는하지 말아야 할 것을 보는 것이 무엇을해야하는지 보는 것만 큼 유용하기 때문에 적어도 부분적으로는 대안을 안내하고자합니다.

Objective-C에는 KVO (key-value observing)라는 값을 관찰하는 시스템이 있습니다. 이 책의 Keypaths 장에서 자세히 다루지 만, binding 문제를 적어도 부분적으로 해결하는 데 사용할 수 있기 때문에 간단히 다루고 싶습니다.

Objective-C의 KVO는 Swift 개발자로 사용할 수 있지만 매우 구체적인 조건에서만 만족합니다.

  • class을 사용합니다.
  • 클래스는 NSObject 에서 상속됩니다.
  • 관찰 가능한 모든 유형이 Objective-C에 표시됩니다. (struct 나 enum은 없습니다.)
  • 관찰 가능한 모든 유형은 @ objc 로 표시됩니다.
  • 관찰 가능한 모든 유형은 dynamic으로 표시됩니다.

그런 다음에만 KVO를 사용할 수 있습니다. User구조체를 다음과 같이 변환합니다.

이제 viewDidLoad () 에 다음 코드를 추가하여 name 속성을 직접 관찰 할 수 있습니다. observer 값은 어딘가에 저장해야합니다. 그렇지 않으면 관찰이 중지됩니다. 예를 들어 다음과 같은 속성을 추가 할 수 있습니다.

var observers = [NSKeyValueObservation]()

그런 다음 observers.append (observer)를 호출하여 관찰자를 안전하게 저장할 수 있습니다.

그러나 이것은 문제의 절반 만 해결합니다. 데이터가 변경되면 View가 업데이트되지만 View가 데이터를 업데이트하도록하려면 어떻게해야합니까? 글쎄요, UIKit에는 여기에 좋은 해결책이 없습니다. KVO와 호환되도록 설계되지는 않았지만 자주 사용할 수 있습니다. 이 경우 UITextField의 text 속성이 모호하게 KVO를 준수하지 않기 때문에 Key 솔루션도 없습니다. 따라서 여전히 돌아가서 포착하는 사용자 지정 하위 클래스를 만들어야합니다. 구식 방식을 바꿉니다. 이 시점에서 KVO가 binding에 대한 매우 끔찍한 접근 방식 인 이유를 알 수 있기를 바랍니다. 탐색하는 것이 개념적으로 흥미로울 수 있지만 그 이상으로 Swift에서 Objective-C를 효과적으로 작성하고 있습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment