public static void NotifyWhenDeallocated(NSObject obj, Action deallocated) { // Add an associated object which will be 'released'd when the obj // to watch is deallocated. When 'release' is sent, then invoke // the 'deallocated' callback. var notifier = new ReleaseNotifier(deallocated); obj.SetAssociatedObject(IntPtr.Zero, notifier, AssociatedObjects.AssociationPolicy.Retain); notifier.Dispose(); // remove any managed references. notifier.enabled = true; // notify on the next 'release' message. }
public bool UsableUntilDeadImpl() { // This test ensure that the main thread can send messages to a garbage collected object, // until the final 'release' message for the managed reference has been sent // (on the main thread). var notifierHandle = IntPtr.Zero; // bool isDeallocated = false; Action deallocated = () => { //Console.WriteLine ("Final release!"); // isDeallocated = true; }; ManualResetEvent isCollected = new ManualResetEvent(false); Action collected = () => { //Console.WriteLine ("Garbage collected!"); isCollected.Set(); }; bool isNotified = false; Action notified = () => { //Console.WriteLine ("Notified"); isNotified = true; }; // Create an object whose handle we store in a local variable. We do not // store the object itself, since we want the object to be garbage collected. var t = new Thread(() => { var obj = new Notifier(collected, notified); ReleaseNotifier.NotifyWhenDeallocated(obj, deallocated); notifierHandle = obj.Handle; }) { IsBackground = true, }; t.Start(); t.Join(); // Now we have a handle to an object that may be garbage collected at any time. int counter = 0; do { GC.Collect(); GC.WaitForPendingFinalizers(); } while (counter++ < 10 && !isCollected.WaitOne(10)); // Now we have a handle to a garbage collected object. if (!isCollected.WaitOne(0)) { // Objects may randomly not end up collected (at least in Boehm), because // other objects may happen to contain a random value pointing to the // object we're waiting to become freed. return(false); } // Send a message to the collected object. Messaging.void_objc_msgSend(notifierHandle, Selector.GetHandle("notify")); Assert.IsTrue(isNotified, "notified"); // We're done. Cleanup. NSRunLoop.Main.RunUntil(NSDate.Now.AddSeconds(0.1)); // Don't verify cleanup, it's not consistent. // And in any case it's not what this test is about. // Assert.IsTrue (isDeallocated, "released"); return(true); }