lppier lppier - 8 days ago 5
iOS Question

Observing NSMutableArray , used Array Accessors but still no luck

I want to observe a NSMutableArray in my class cSoundChannel. Hence after reading this post
Observing an NSMutableArray for insertion/removal
I implemented the key observing in this manner.

For cSoundChanel class,

My property for the mutable array is

@property (assign, nonatomic) NSMutableArray* midiDevices;


The functions I introduced using kvo array accessors in the class are as follows :

- (void) addmidiDevicesObject:(NSObject *) str {
[self insertObject:str inMidiDevicesAtIndex:[_midiDevices count]];
}

- (void)insertObject:(NSObject *)str inMidiDevicesAtIndex:(NSUInteger)index {
[self.midiDevices insertObject:str atIndex:index];
return;
}


For my ViewController.m file, where I need to observe midiDevices, I did the following.

[self.cSoundChannel addObserver:self forKeyPath:@"midiDevices" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];


and expected to be able to observe the mutable array in ...

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"midiDevices"]) {
NSLog(@"Let's see!");
}
}


but alas... it did not print "Let's see!"
Observing other things... NSString etc works...

Is there anything that I missed out? Help!

Answer

Your code is not fully KVC compliant on midiDevices. You also need to implement removeObjectFromMidiDevicesAtIndex::

- (void)removeObjectFromMidiDevicesAtIndex:(NSUInteger)index {
  [self.midiDevices removeObjectAtIndex:index];
}

See the Key-Value Coding Programming Guide for full details. Specifically see Mutable Indexed Accessors.


EDIT: The following example code demonstrates what I'm describing, and prints "Let's see!" as expected. Removing removeObjectFromMidiDevicesAtIndex: will re-introduce the bug seen in the OP's code.

#import <Foundation/Foundation.h>

@interface Observed : NSObject
@property (nonatomic) NSMutableArray* midiDevices;
@end

@implementation Observed

- (id)init {
  self = [super init];
  if (self) {
    _midiDevices = [NSMutableArray new];
  }
  return self;
}

- (void) addmidiDevicesObject:(NSObject *) str {
  [self insertObject:str inMidiDevicesAtIndex:[_midiDevices count]];
}

- (void)insertObject:(NSObject *)str inMidiDevicesAtIndex:(NSUInteger)index {
  [self.midiDevices insertObject:str atIndex:index];
}

- (void)removeObjectFromMidiDevicesAtIndex:(NSUInteger)index {
  [self.midiDevices removeObjectAtIndex:index];
}

@end

@interface Observer : NSObject
@property (nonatomic) Observed *observed;
@end

@implementation Observer

- (id)init {
  self = [super init];
  if (self) {
    _observed = [Observed new];
    [_observed addObserver:self forKeyPath:@"midiDevices" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
  }
  return self;
}

- (void)dealloc {
  [_observed removeObserver:self forKeyPath:@"midiDevices"];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  if ([keyPath isEqualToString:@"midiDevices"]) {
    NSLog(@"Let's see!");
  }
}

@end

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    Observer *observer = [Observer new];
    [observer.observed addmidiDevicesObject:@"test"];
  }
  return 0;
}