Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c NSN通知发送一次,但收到多次_Objective C_Cocoa Touch_Nsnotificationcenter - Fatal编程技术网

Objective c NSN通知发送一次,但收到多次

Objective c NSN通知发送一次,但收到多次,objective-c,cocoa-touch,nsnotificationcenter,Objective C,Cocoa Touch,Nsnotificationcenter,我正在两个班级之间与NSNotificationCenter进行交流。我的问题是,尽管我点击了一次按钮(该按钮只会触发一次),但我无意中从一次对NSNotificationCenter的呼叫中生成了越来越多的通知 下面是对该问题的更好解释,代码如下: 我的两个类是主视图类和菜单类 点击mainView类中的视图时,它将启动一个由菜单创建和管理的视图。初始化mainView时调用此代码: menu=[[MyMenu alloc] init]; UITapGestureRecognizer * t

我正在两个班级之间与NSNotificationCenter进行交流。我的问题是,尽管我点击了一次按钮(该按钮只会触发一次),但我无意中从一次对NSNotificationCenter的呼叫中生成了越来越多的通知

下面是对该问题的更好解释,代码如下:


我的两个类是主视图类和菜单类

点击mainView类中的视图时,它将启动一个由菜单创建和管理的视图。初始化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];
}
- (void) displayMenu {
    [viewForMenu addSubview:menuView];
}
- (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;
}
- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
    NSInteger buttonTapped=[[recognizer view] tag];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ItemChange" object:nil userInfo:@{@"selected":@(buttonTapped)}];
[self clearMenu];
}
- (void) onChangeItem: (NSNotification*) notification {
    NSLog(@"change item to %d",[[[notification userInfo] objectForKey:@"clock"] intValue]);
}
以下是菜单类初始化的方式:

- (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;
}
这是
displayMenu
类中的菜单方法:

- (void) onTapped: (UIGestureRecognizer*) recognizer {
    NSLog(@"tap");
    [menu displayMenu];
}
- (void) displayMenu {
    [viewForMenu addSubview:menuView];
}
- (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;
}
- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
    NSInteger buttonTapped=[[recognizer view] tag];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ItemChange" object:nil userInfo:@{@"selected":@(buttonTapped)}];
[self clearMenu];
}
- (void) onChangeItem: (NSNotification*) notification {
    NSLog(@"change item to %d",[[[notification userInfo] objectForKey:@"clock"] intValue]);
}
菜单类还有一个
clearMenu
方法:

- (void) clearMenu {
    [menuView removeFromSuperview];
}
这是UICollectionView中每个单元格的代码,包含在我的菜单类中:

- (void) onTapped: (UIGestureRecognizer*) recognizer {
    NSLog(@"tap");
    [menu displayMenu];
}
- (void) displayMenu {
    [viewForMenu addSubview:menuView];
}
- (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;
}
- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
    NSInteger buttonTapped=[[recognizer view] tag];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ItemChange" object:nil userInfo:@{@"selected":@(buttonTapped)}];
[self clearMenu];
}
- (void) onChangeItem: (NSNotification*) notification {
    NSLog(@"change item to %d",[[[notification userInfo] objectForKey:@"clock"] intValue]);
}
这将调用
onButtonTapped:
方法,也在我的菜单中类中:

- (void) onTapped: (UIGestureRecognizer*) recognizer {
    NSLog(@"tap");
    [menu displayMenu];
}
- (void) displayMenu {
    [viewForMenu addSubview:menuView];
}
- (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;
}
- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
    NSInteger buttonTapped=[[recognizer view] tag];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ItemChange" object:nil userInfo:@{@"selected":@(buttonTapped)}];
[self clearMenu];
}
- (void) onChangeItem: (NSNotification*) notification {
    NSLog(@"change item to %d",[[[notification userInfo] objectForKey:@"clock"] intValue]);
}
此通知由mymainView类使用以下代码拾取:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onChangeItem:) name:@"ItemChange" object:nil];
这将调用mymainView类中的
onChangeItem:
方法:

- (void) onTapped: (UIGestureRecognizer*) recognizer {
    NSLog(@"tap");
    [menu displayMenu];
}
- (void) displayMenu {
    [viewForMenu addSubview:menuView];
}
- (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;
}
- (void) onButtonTapped:(UIGestureRecognizer*) recognizer {
    NSInteger buttonTapped=[[recognizer view] tag];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ItemChange" object:nil userInfo:@{@"selected":@(buttonTapped)}];
[self clearMenu];
}
- (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
我第三次听到这个:

...[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次通知-但接收类收到多个通知。

谁能给我解释一下吗


抱歉发了这么长的帖子

好吧,我假设
[[NSNotificationCenter defaultCenter]addObserver:self-selector:@selector(onchangeem:)name:@“ItemChange”object:nil]被多次添加

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

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

这样一来,将只有一个观察者回叫。

问题:我遇到了同样的问题,观察者回叫了两次,有时是三次

场景

  • 用户点击注销按钮
  • HomeViewController
    被取消,出现了
    LoginViewController
    屏幕
  • 当用户第二次再次登录时
  • 观察员被叫来两次(有时三次)
  • 问题是
    [[NSNotificationCenter defaultCenter]removeObserver:self]HomeViewController
    dealloc
    方法中的code>根本没有被调用,这实际上会从通知中心一起删除一个对象

    ℹ️ <代码>解除锁定
    是由 当对象不再为其所有时,对象的Objective-C运行时 应用程序的任何部分

    解决方案:创建自己的方法
    dispose
    ,并在用户点击
    注销时调用该方法

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

    其中,在您调用的代码中,
    [[NSNotificationCenter defaultCenter]addObserver:自选择器:@selector(onChangeItem:)name:@“ItemChange”对象:nil]并删除它?在您的
    onChangeItem:
    方法中,打印正在观察通知
    NSLog(@“%p”,self)的
    self
    的内存地址并显示您在控制台中看到的内容。这在初始化mainView时调用。所以它只被调用一次。您可以添加
    NSLog(@“%p”,self)
    在您的
    onChangeItem:
    方法中,并向我们展示您在控制台中看到的内容每次重复使用单元格时,您都会向单元格添加一个新的点击手势识别器。在添加一个之前,您应该先检查单元格中是否存在一个。@rdelmar如何检查是否存在一个?@Jimmery如果您对“addObserver”进行全局搜索,每个单元格上方是否有一个“removeObserver:”?哦。好啊我是个白痴,我把你的代码放错地方了。现在,它在正确的地方,它修复了我的错误!非常感谢:)我希望我能提供给你的不仅仅是虚假的互联网积分!我也有同样的问题,但我确信我只调用了addObserver一次,但我得到了两次回调,4年后使用了完全相同的通知对象(通知具有相同的内存地址和内容),并且你得到了更多的假互联网点!这帮我摆脱了困境!