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

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

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

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

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

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

-(void)ArrayUpdatedNotification:(NSMutableArray*)array
{
    //Reload table or do something
} 

提前致谢。

【问题讨论】:

    标签: objective-c iphone uitableview nsmutablearray


    【解决方案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 更新了我的答案!
    猜你喜欢
    • 2012-01-21
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-09
    相关资源
    最近更新 更多