How to add observer on NSMutableArray?

前端 未结 3 2128
天命终不由人
天命终不由人 2020-12-24 04:44

I have searched a lot but didn\'t find useful code or tutorial.

In my application, I have an mutable array which update in every 60 seconds.

The objects in a

3条回答
  •  猫巷女王i
    2020-12-24 05:18

    You can abstract the array into a data container class with accessor methods, and then use key-value observing to observe when the array that backs the container object is changed (you cannot use KVO on an NSArray directly).

    A simple example of a class used as an abstraction on top of an array follows. You use its insertObject:inDataAtIndex: and removeObjectFromDataAtIndex: methods instead of directly accessing the with addObject: and removeObject:.

    // DataContainer.h
    @interface DataContainer : NSObject
    
    // Convenience accessor
    - (NSArray *)currentData;
    
    // For KVC compliance, publicly declared for readability
    - (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index;
    - (void)removeObjectFromDataAtIndex:(NSUInteger)index;
    - (id)objectInDataAtIndex:(NSUInteger)index;
    - (NSArray *)dataAtIndexes:(NSIndexSet *)indexes;
    - (NSUInteger)countOfData;
    
    @end
    
    // DataContainer.m
    
    @interface DataContainer ()
    
    @property (nonatomic, strong) NSMutableArray *data;
    
    @end
    
    @implementation DataContainer
    
    //  We'll use automatic notifications for this example
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
    {
        if ([key isEqualToString:@"data"]) {
            return YES;
        }
        return [super automaticallyNotifiesObserversForKey:key];
    }
    
    - (id)init
    {
        self = [super init];
        if (self) {
            // This is the ivar which provides storage
            _data = [NSMutableArray array];
        }
        return self;
    }
    
    //  Just a convenience method
    - (NSArray *)currentData
    {
        return [self dataAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self countOfData])]];
    }
    
    //  These methods enable KVC compliance
    - (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
    {
        self.data[index] = object;
    }
    
    - (void)removeObjectFromDataAtIndex:(NSUInteger)index
    {
        [self.data removeObjectAtIndex:index];
    }
    
    - (id)objectInDataAtIndex:(NSUInteger)index
    {
        return self.data[index];
    }
    
    - (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
    {
        return [self.data objectsAtIndexes:indexes];
    }
    
    - (NSUInteger)countOfData
    {
        return [self.data count];
    }
    
    @end
    

    The reason that we do this is so we can now observe changes made to the underlying array. This is done through Key Value Observing. A simple view controller that instantiates and observes a data controller is shown:

    // ViewController.h
    @interface ViewController : UIViewController
    
    @end
    
    // ViewController.m
    
    @interface ViewController ()
    
    @property (nonatomic,strong) DataContainer *dataContainer;
    
    @end
    
    @implementation ViewController
    
    static char MyObservationContext;
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            //  Instantiate a DataContainer and store it in our property
            _dataContainer = [[DataContainer alloc] init];
            //  Add self as an observer. The context is used to verify that code from this class (and not its superclass) started observing.
            [_dataContainer addObserver:self
                             forKeyPath:@"data"
                                options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
                                context:&MyObservationContext];
        }
    
        return self;
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        //  Check if our class, rather than superclass or someone else, added as observer
        if (context == &MyObservationContext) {
            //  Check that the key path is what we want
            if ([keyPath isEqualToString:@"data"]) {
                //  Verify we're observing the correct object
                if (object == self.dataContainer) {
                    NSLog(@"KVO for our container property, change dictionary is %@", change);
                }
            }
        }
        else {
            //  Otherwise, call up to superclass implementation
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        //  Insert and remove some objects. Console messages should be logged.
        [self.dataContainer insertObject:[NSObject new] inDataAtIndex:0];
        [self.dataContainer insertObject:[NSObject new] inDataAtIndex:1];
        [self.dataContainer removeObjectFromDataAtIndex:0];
    }
    
    - (void)dealloc
    {
        [_dataContainer removeObserver:self forKeyPath:@"data" context:&MyObservationContext];
    }
    
    @end
    

    When this code runs, three changes to the data are observed by the view controller and logged to the console:

    KVO for our container property, change dictionary is {
            indexes = "[number of indexes: 1 (in 1 ranges), indexes: (0)]";
            kind = 2;
            new =     (
                ""
            );
        }
    KVO for our container property, change dictionary is {
            indexes = "[number of indexes: 1 (in 1 ranges), indexes: (1)]";
            kind = 2;
            new =     (
                ""
            );
        }
    KVO for our container property, change dictionary is {
            indexes = "[number of indexes: 1 (in 1 ranges), indexes: (0)]";
            kind = 3;
            old =     (
                ""
            );
        }
    

    While this is somewhat complex (and can get much more involved), this is the only way to be notified automatically that a mutable array's contents were changed.

提交回复
热议问题