12

我正在使用 NSNotificationCenter 在两个类之间进行通信。我的问题是,虽然我点击了一次按钮(并且该按钮只触发一次),但我无意中从一次调用 NSNotificationCenter 中产生了越来越多的通知。

这是对问题的更好解释,带有代码:


我的两个类是mainView类和Menu类。

当点击mainView类中的视图时,它会启动由Menu类创建和管理的视图。初始化mainView时调用此代码:

menu=[[MyMenu alloc] init];
UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapped:)];
[tap setNumberOfTapsRequired:1];
[container addGestureRecognizer:tap];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onChangeItem:) name:@"ItemChange" object:nil];

此手势识别器触发此方法,也在mainView类中:

- (void) onTapped: (UIGestureRecognizer*) recognizer {
    NSLog(@"tap");
    [menu displayMenu];
}

这是Menu类的初始化方式:

- (MyMenu*) init {
    self=[super init];
    UICollectionViewFlowLayout * layout=[[UICollectionViewFlowLayout alloc] init];
    menuView=[[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 200, 200) collectionViewLayout:layout];
    [menuView setDataSource:self];
    [menuView setDelegate:self];
    [menuView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    [menuView setAutoresizesSubviews:YES];
    [menuView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
    [menuView setBackgroundColor:[UIColor clearColor]];
    [menuView setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
    return self;
}

这是Menu类中的displayMenu方法:

- (void) displayMenu {
    [viewForMenu addSubview:menuView];
}

Menu类也有一个clearMenu方法:

- (void) clearMenu {
    [menuView removeFromSuperview];
}

这是 UICollectionView 中每个单元格的代码,包含在我的Menu类中:

- (UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell * cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    [cell setTag:indexPath.row];
    UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onButtonTapped:)];
    [tap setNumberOfTapsRequired:1];
    [cell addGestureRecognizer:tap];
    NSLog(@"button tapped : %d",indexPath.row);
    return cell;
}

这调用了该onButtonTapped:方法,也在我的Menu类中:

- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
    NSInteger buttonTapped=[[recognizer view] tag];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ItemChange" object:nil userInfo:@{@"selected":@(buttonTapped)}];
[self clearMenu];
}

我的mainView类使用以下代码获取此通知:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onChangeItem:) name:@"ItemChange" object:nil];

这在我的mainView类中调用该onChangeItem:方法:

- (void) onChangeItem: (NSNotification*) notification {
    NSLog(@"change item to %d",[[[notification userInfo] objectForKey:@"clock"] intValue]);
}

这就是代码。


好的,问题来了:第一次显示菜单时,我在日志中得到了这个:

...[43023:11f03] tap
...[43023:11f03] button tapped : 1
...[43023:11f03] change item to 1

这很好,这就是我所期望的。但是第二次我得到了这个:

...[43023:11f03] tap
...[43023:11f03] button tapped : 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1

第三次我得到这个:

...[43023:11f03] tap
...[43023:11f03] button tapped : 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1
...[43023:11f03] change item to 1

等等。对菜单项的每次连续点击都会使通知呼叫量翻倍。


一开始我以为我是在添加多个视图,因此会导致多个按钮点击,从而导致多个通知调用。

但是,正如您从我的日志中看到的那样,情况并非如此。按钮只接收 1 个点击事件 - 这只会触发 1 个通知 - 但接收类会收到多个通知。

谁能给我解释一下?

对不起,冗长的帖子!

4

2 回答 2

36

好吧,我假设[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onChangeItem:) name:@"ItemChange" object:nil];被添加了不止一次。

我喜欢在添加观察者之前删除任何潜在的观察者,如下所示:

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"ItemChange" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onChangeItem:) name:@"ItemChange" object:nil];

这样一来,将永远只有一个观察者回调。

于 2013-08-07T17:08:16.870 回答
1

问题:我遇到了同样的问题,观察者打电话两次,有时三次。

设想

  1. 用户点击注销按钮
  2. HomeViewController被解雇并LoginViewController出现屏幕
  3. 当用户第二次再次登录时
  4. 观察者被调用两次(有时三次)

问题[[NSNotificationCenter defaultCenter] removeObserver:self];在于根本没有调用dealloc我的方法,它实际上从通知中心一起删除了一个对象。HomeViewController

ℹ️dealloc是一个 Objective-C 选择器,当对象不再由应用程序的任何部分拥有时,它由 Objective-C 运行时发送到对象。

解决方案: 制作自己的方法dispose并在用户点击时调用它logout

- (void)dealloc { 
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // ....
}

- (void)dispose { 
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)logoutTapped { 
    [self dispose];
    // ....
}
于 2018-10-09T11:36:04.320 回答