     @observeMutations obj[keypath], obj[@id], keypath
 
   unobserve: (obj, keypath, callback) ->
-    callbacks = @weakmap[obj[@id]].callbacks[keypath]
+    map = @weakmap[obj[@id]]
+    callbacks = map.callbacks[keypath]
 
-    idx = callbacks.indexOf(callback);
-    if idx >= 0
+    if (idx = callbacks.indexOf(callback)) >= 0
       callbacks.splice idx, 1
+
+      unless callbacks.length
+        delete map.callbacks[keypath]
+
     @unobserveMutations obj[keypath], obj[@id], keypath
+    @cleanupWeakReference map, obj[@id]
 
   get: (obj, keypath) ->
     obj[keypath]

        map.pointers[ref].push keypath

  unobserveMutations: (obj, ref, keypath) ->
    if Array.isArray obj and obj[@id]?
      if keypaths = @weakReference(obj).pointers?[ref]
        idx = keypaths.indexOf(keypath)
        if idx >= 0
          keypaths.splice idx, 1

  observe: (obj, keypath, callback) ->
    callbacks = @weakReference(obj).callbacks

        get: -> value
        set: (newValue) =>
          if newValue isnt value
            @unobserveMutations value, obj[@id], keypath
            value = newValue
            callback() for callback in callbacks[keypath].slice() when callback in callbacks[keypath]
            @observeMutations newValue, obj[@id], keypath
