场景:订单提交成功后pop返回上级页面,并弹出下一步功能操作的提示弹窗,多次反复提交订单发现提示弹窗会重复弹出多次,说明通知被监听到了多次。
原因分析:详情页面提交订单成功注册发送通知给列表控制器并pop返回,列表控制器监听到通知后弹窗,这时被监听的通知并没有被移除,再重复操作,由于第一次的被监听通知没有移除,所以后续的操作会一直累加重复监听。
通知的使用步骤:创建通知、发送通知、移除通知
监听通知有两种方法:
1. - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject
2. - (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block
建议在viewDidLoad方法中创建和监听通知,因为此方法只走一次可防止重复创建
一、- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject
方法
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
#1.注册通知监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(publishFindGoodsSuccessNotAc:) name:@"PublishPreciseProcurementSuccessNotification"object:nil];
}
#2.监听通知执行方法
- (void)publishFindGoodsSuccessNotAc:(NSNotification *)not{
NSLog(@"弹窗");
}
#3.发送通知(通知发送时可传参)
[[NSNotificationCenter defaultCenter] postNotificationName:@"PublishPreciseProcurementSuccessNotification" object:@1];
#4.移除通知
//移除通知一般在dealloc中实现,因为应用支持手势返回,滑回一半又返回等操作,在页面真正销毁的时候移除最合适
//移除有两种方法,一个是移除当前页面所有的通知,一种移除指定的通知,如下:
- (void)dealloc{
// 移除当前控制器所有通知
[[NSNotificationCenter defaultCenter] removeObserver:self];
//移除名为@"PublishPreciseProcurementSuccessNotification"的那个通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"PublishPreciseProcurementSuccessNotification"object:nil];
}
实际中发现dealloc方法有时会不走,一般有以下三种原因可以参考排查
1. ViewController 中有Block,Block里面存在强引用;
2. ViewController 中有关的代理 ,delegate的属性应该用assign/weak修饰;
3. ViewController 中存在NSTimer ,计时器没有及时销毁;
二、- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block
方法
该方法有个block,要操作的步骤可以直接写在block里面 使用较第一种方法方便
///声明通知观察者
@property (weak, nonatomic) id preciseObserve;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
#1.注册通知监听
//Name: 通知的名称
//object:谁发出的通知
//queue: 队列,决定 block 在哪个线程中执行, nil 在发布通知的线程中执行
//usingBlock: 只要监听到通知,就会执行这个 block
#该方法有个block,要操作的步骤可以直接写在block里面 使用较第一种方法方便
self.preciseObserve = [[NSNotificationCenter defaultCenter] addObserverForName:@"PublishPreciseProcurementSuccessNotification"object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
@strongify(self);
[self publishFindGoodsSuccessNotAc:note];
}];
}
#2.发送通知(与第一种方法相同)
#3.移除通知
- (void)dealloc {
//移除观察者 preciseObserve
[[NSNotificationCenter defaultCenter] removeObserver:self.preciseObserve];
}
通过比较移除的方法,可以知道,为什么第二种创建通知方法时要有一个id类型的观察者接收值,不要用第一种的注销方法,这也是我出现重复弹框bug的原因,我创建的时候没用一个观察者接收,直接写:
//用第二种创建监听方法,错误的移除示例
[[NSNotificationCenter defaultCenter] addObserverForName:@"PublishPreciseProcurementSuccessNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"收到通知");
@strongify(self);
[self publishFindGoodsSuccessNotAc:note];
}];
导致通知未移除而重复创建,执行方法一次比一次多,而移除的时候用
- (void)dealloc{
//移除名为PublishPreciseProcurementSuccessNotification的通知
[[NSNotificationCenter defaultCenter] removeObserver:self.preciseObserve name:@"PublishPreciseProcurementSuccessNotification" object:nil];
}
需要注意销毁应该是哪里监听哪里销毁,不要在发送通知的控制器里执行销毁!!
欢迎大佬儿来指正纠错,共同学习!!
网友评论