@@ -9,7 +9,7 @@ func delay(_ delay:Double, closure:@escaping ()->()) {
99
1010
1111// observed object must derive from NSObject
12- class MyClass1 : NSObject {
12+ class Observed : NSObject {
1313 // absolutely crucial to say "dynamic" or this won't work (and now objc too)
1414 @objc dynamic var value : Bool = false
1515 deinit {
@@ -19,7 +19,7 @@ class MyClass1 : NSObject {
1919
2020// the observer no longer has to derive from NSObject, or even be a class instance!
2121// that's because it isn't the observer; the real observer is the NSKeyValueObservation object
22- class MyClass2 {
22+ class Observer {
2323
2424// failed experiment
2525
@@ -30,7 +30,7 @@ class MyClass2 {
3030
3131 var obs = Set < NSKeyValueObservation > ( )
3232
33- func registerWith( _ mc: MyClass1 ) {
33+ func registerWith( _ mc: Observed ) {
3434 let opts : NSKeyValueObservingOptions = [ . old, . new]
3535 let ob = mc. observe ( \. value, options: opts) { obj, change in
3636 // obj is the observed object
@@ -62,8 +62,8 @@ private var con = "ObserveValue"
6262
6363@UIApplicationMain
6464class AppDelegate : UIResponder , UIApplicationDelegate {
65- var objectA : MyClass1 !
66- var objectB : MyClass2 !
65+ var observed : Observed !
66+ var observer : Observer !
6767 var window : UIWindow ?
6868
6969 func application( _ application: UIApplication , didFinishLaunchingWithOptions launchOptions: [ UIApplication . LaunchOptionsKey : Any ] ? ) -> Bool {
@@ -75,15 +75,14 @@ class AppDelegate : UIResponder, UIApplicationDelegate {
7575
7676
7777
78- objectA = MyClass1 ( )
79- objectB = MyClass2 ( )
78+ observed = Observed ( )
79+ observer = Observer ( )
8080 // step one: registration
81- let kp = \MyClass1 . value
82- // objectB.registerWith(objectA, keyPath:kp)
83- objectB. registerWith ( objectA)
81+ let kp = \Observed . value
82+ observer. registerWith ( observed)
8483
8584 // step two: make a change in a KVO compatible way
86- objectA . value = true
85+ observed . value = true
8786
8887 // look ma, no memory management! no unregistration!
8988 // I can safely destroy one or both objects, in either order
@@ -93,40 +92,40 @@ class AppDelegate : UIResponder, UIApplicationDelegate {
9392 switch which {
9493 case 0 :
9594 // A then B
96- objectA = nil
97- objectB = nil
95+ observed = nil
96+ observer = nil
9897 // but that _will_ crash in iOS 10 or before!
9998 case 1 :
10099 // B then A, with observed change
101- objectB = nil
100+ observer = nil
102101 // ha ha! when objectB goes out of existence, so does the observer!
103102 // thus the observation stops
104- objectA . value = false
105- objectA = nil
103+ observed . value = false
104+ observed = nil
106105 // but that last line will crash in iOS 10 if the notification function mentions self
107106 // and even in iOS 11, objectB is leaking if the notification function mentions self
108107 // solution is to use unowned self in the notification function
109108 case 2 :
110109 // premature deregistration 1
111- objectB . obs. removeAll ( )
112- objectA . value = false
110+ observer . obs. removeAll ( )
111+ observed . value = false
113112 case 3 :
114113 // premature deregistration 2
115- objectB . obs. forEach { $0. invalidate ( ) }
116- objectA . value = false
114+ observer . obs. forEach { $0. invalidate ( ) }
115+ observed . value = false
117116 case 4 :
118117 // let's artificially keep the observer alive and see what happens
119- let obs = objectB . obs
120- objectB = nil
118+ let obs = observer . obs
119+ observer = nil
121120 delay ( 1 ) {
122121 let obs2 = obs // ensure life
123- self . objectA . value = false
122+ self . observed . value = false
124123 _ = obs2
125124 }
126125 // crash because of the unowned self, as expected
127126 default :
128127 // just proving that this would have worked if we had done nothing
129- objectA . value = false
128+ observed . value = false
130129 }
131130
132131 return true
0 commit comments