Key-Value Observe in Swift not showing insertions and removals in arrays

后端 未结 1 781
抹茶落季
抹茶落季 2020-12-30 07:53

I created a class which contains an array. I added an observer to that array in a view controller and performed some modifications to that array.

The problem is tha

相关标签:
1条回答
  • 2020-12-30 08:30

    According to the Swift guide:

    For arrays, copying only takes place when you perform an action that has the potential to modify the length of the array. This includes appending, inserting, or removing items, or using a ranged subscript to replace a range of items in the array.

    Take as an example an append operation. Under the hood, when you append to your array, Swift creates a new array in memory, copies the items from the existing animals array into this new array - plus the new item - then assigns this new array to the animals variable. This sleight-of-hand is why you only ever get the kind of 1 (Setting), because in fact each 'edit' actually results in a new array being created.

    It's different with NSMutableArray because the behaviour is a bit more intuitive - edits are made to the existing array (there's no behind-the-scenes copying to a new array), so the array that exists after the edit is the same array that existed before it - hence the value stored in the change dictionary with the key NSKeyValueChangeKindKey can be one of .Insertion, .Removal, etc.

    Even that isn't the whole story however, because the way you make an KVO compliant changes to an NSMutableArray varies depending on whether you're using Swift or Objective-C. In Objective-C, Apple strongly recommend implementing what they call the optional mutable indexed accessors. These are just methods with a standard signature that make KVO-compliant changes in a very efficient way.

    // Mutable Accessors ///////////////////////////////////////////
    
    // This class has a property called <children> of type NSMutableArray
    
    // MUST have this (or other insert accessor)
    -(void)insertObject:(NSNumber *)object inChildrenAtIndex:(NSUInteger)index {
        [self.children insertObject:object atIndex:index];
    }
    
    // MUST have this (or other remove accessor)
    -(void)removeObjectFromChildrenAtIndex:(NSUInteger)index {
        [self.children removeObjectAtIndex:index];
    }
    
    // OPTIONAL - but a good idea.
    -(void)replaceObjectInChildrenAtIndex:(NSUInteger)index withObject:(id)object {
        [self.children replaceObjectAtIndex:index withObject:object];
    }
    

    Swift doesn't appear to have these, so the only way to make KVO-compliant changes is via the mutable proxy object - described in the NSKeyValueCoding page. Here's a very quick example:

    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate {
    
        dynamic var children: NSMutableArray = NSMutableArray()
    
    
        ////////////////////////////////////
    
        func applicationDidFinishLaunching(aNotification: NSNotification) {
    
            addObserver(self,
                forKeyPath: "children",
                options: .New | .Old,
                context: &Update)
    
            // Get the KVO/KVC compatible array
            var childrenProxy = mutableArrayValueForKey("children")
    
            childrenProxy.addObject(NSNumber(integer: 20)) // .Insertion
    
            childrenProxy.addObject(NSNumber(integer: 30)) // .Insertion
    
            childrenProxy.removeObjectAtIndex(1)  // .Removal
        }
    }
    
    0 讨论(0)
提交回复
热议问题