如何在 NSMutableArray 上添加观察者?

Posted

技术标签:

【中文标题】如何在 NSMutableArray 上添加观察者?【英文标题】:How to add observer on NSMutableArray? 【发布时间】:2013-03-25 10:21:28 【问题描述】:

我搜索了很多,但没有找到有用的代码或教程。

在我的应用程序中,我有一个每 60 秒更新一次的可变数组。

数组中的对象被多个视图控制器中的表视图显示。

我想仅在数组中的值更改或更新时自动重新加载表格视图。

为此,我想在可变数组上添加观察者,即当数组中的值发生变化时,它应该调用特定的方法,例如

-(void)ArrayUpdatedNotification:(NSMutableArray*)array

    //Reload table or do something
 

提前致谢。

【问题讨论】:

【参考方案1】:

您可以将数组抽象为具有访问器方法的数据容器类,然后使用键值观察来观察支持容器对象的数组何时更改(您不能直接在NSArray上使用KVO)。

下面是一个简单的类示例,该类用作数组顶部的抽象。您使用它的insertObject:inDataAtIndex:removeObjectFromDataAtIndex: 方法,而不是直接使用addObject: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

我们这样做的原因是我们现在可以观察对底层数组所做的更改。这是通过Key Value Observing 完成的。显示了一个实例化和观察数据控制器的简单视图控制器:

// 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

当这段代码运行时,视图控制器会观察到数据的三个变化并记录到控制台:

KVO for our container property, change dictionary is 
        indexes = "<NSIndexSet: 0x8557d40>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
        kind = 2;
        new =     (
            "<NSObject: 0x8557d10>"
        );
    
KVO for our container property, change dictionary is 
        indexes = "<NSIndexSet: 0x715d2b0>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
        kind = 2;
        new =     (
            "<NSObject: 0x71900c0>"
        );
    
KVO for our container property, change dictionary is 
        indexes = "<NSIndexSet: 0x8557d40>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
        kind = 3;
        old =     (
            "<NSObject: 0x8557d10>"
        );
    

虽然这有点复杂(并且可能涉及更多),但这是自动通知可变数组的内容已更改的唯一方法。

【讨论】:

比你更好的答案【参考方案2】:

可以做的是 - 更新您的阵列后发送通知 (NSNotificationCenter),所有控制器都会收到此通知。收到通知后,控制器应该执行 [tableview reloaddata]。

代码示例

// Adding an observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTable:) name:@"arrayUpdated" object:nil];

// Post a notification
[[NSNotificationCenter defaultCenter] postNotificationName:@"arrayUpdated" object:nil]; 

// the void function, specified in the same class where the Notification addObserver method has defined
- (void)updateTable:(NSNotification *)note  
    [tableView reloadData]; 

【讨论】:

谢谢先生/女士。当然我们可以使用 NSNotificationCenter。但是你能告诉我怎么做吗,我试着用谷歌搜索,但没有找到正确的方向。你能给我一些示例代码吗? // 添加观察者 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTable:) name:@"arrayUpdated" object:nil]; // 发布通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"arrayUpdated" object:nil]; // void 函数,在定义了 Notification addObserver 方法的同一个类中指定 - (void)updateTable:(NSNotification *)note [tableView reloadData]; 谢谢罗汉。我会试试的。【参考方案3】:

如果你想使用闪亮的方块,你可以这样做

// Create an instance variable for your block holder in your interface extension
@property (strong) id notificationHolder;

// Listen for notification events (In your TableView class.
self.notificationHolder = [[NSNotificationCenter defaultCenter] addObserverForName:@"NotificationName"
                             object:nil
                              queue:[NSOperationQueue mainQueue]
                         usingBlock:^(NSNotification *note) 

        NSLog(@"Received notification");
];

然后在 dealloc 中(或者当你不再使用它时)

- (void)dealloc 
     [[NSNotificationCenter defaultCenter] removeObserver:self.notificationHolder];

然后在其他类中

// Send a notification
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];

如果有不清楚的地方请教!希望对您有所帮助!

因评论而编辑

YourEvent”是通知的名称,这意味着您可以随意命名。 (也许“UpdateArrayNotification 可能是个好名字?)

需要考虑的事项:请注意,同一通知可以有多个观察者。这意味着一个“帖子”将被所有观察者抢购一空。

【讨论】:

谢谢菲利普。什么是“你的事件”?你能画出名字为tableView和mutableArray的代码吗?这样我可以更好地理解。谢谢 @DAddict 更新了我的答案!

以上是关于如何在 NSMutableArray 上添加观察者?的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式添加到 NSMutableArray 并将其显示在 uitableviewcells 上?

按字母顺序对 NSMutableArray 进行排序[重复]

如何将json数据中的对象添加到nsmutablearray?

如何在Objective C中将对象添加到NSMutableArray?

如何将唯一对象添加到 nsmutablearray?

在 NSMutableArray 的特定索引处添加对象