美文网首页
NSNotificationCenter的理解和运用 教程【转】

NSNotificationCenter的理解和运用 教程【转】

作者: 小船2022 | 来源:发表于2023-03-09 08:50 被阅读0次

    通知中心:实现了一对多的消息传递,可以实现跨页面传递。

    cocoa消息机制算是同步的,观察者只要向消息中心注册,即可接受其他对象发送来的消息,消息发送者和消息接受者两者可以互相一无所知,完全解耦。这种消息机制可以用应用于任意时间和任何对象,观察者可以有多个。所以消息具有广播的性质,注意:观察者向消息中心注册以后,在不需要接受消息时需要向中心注销,属于典型的观察者模式。

    消息通知中重要的两个类:

    1)NSNotificationCenter:实现NSNotificationCenter的原理是一个观察者模式,获得NSNotificationCenter的方法只有一种,那就是[NSNotificationCenter defaultCenter],通过调用静态方法defaultCenter就可以获取这个通知中心的对象了。NSNotificationCenter 是一个单例模式,而这个通知中心的对象会一直存在于一个应用的生命周期。

    2)NSNotification:这是消息携带的载体,通过它,可以把消息内容传递给观察者。

    NSNotificationCenter的主要方法有以下几种:

    @property (class, readonly, strong) NSNotificationCenter *defaultCenter;

    - (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender

    //。1.notificationObserver不能为nil

      //  2.notificationSelector回调方法有且只有一个参数(NSNotifcation对象)。

      //。3.监听同一条通知的多个观察者,在通知到达时,它们执行回调的顺序是不确定的,所以我们不能去假设操作的执行会按照添加观察者的顺序来执行。

    - (void)postNotification:(NSNotification *)notification;

    - (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;

    - (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

    - (void)removeObserver:(id)observer;

    - (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;

    - (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);

        // The return value is retained by the system, and should be held onto by the caller in

        // order to remove the observer with removeObserver: later, to stop observation.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    其中最后一个方法返回NSObserver对象,在使用此方法添加观察者的时候,需要注意循环引用和remove的方式。每种方法都在不同系统上的表现也不尽相同。

    一、添加observer的方法

    1.SEL方式

    - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

    1

    此方法中的observer是弱引用,直接使用self等不会造成强引用。

    提示:如果同一个通知连着添加n次,那SEL的方法会调用n次,所以要确保addOnserver和removeObserver成对的出现

    2.Block方式

    - (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);

        // The return value is retained by the system, and should be held onto by the caller in

        // order to remove the observer with removeObserver: later, to stop observation.

    1

    2

    3

    注意:1)这个方法会返回一个NSObserver对象,和这个对象被系统强持有,调用者需要持有这个对象,拥于停止通知移除观察者时使用。这种方式添加的通知,如果不持有这个对象,是无法彻底销毁这个通知的,具体做法如下:

      @property (nonatomic, strong) id observer;

      - (void)viewDidLoad {

        [super viewDidLoad];

        // Do any additional setup after loading the view, typically from a nib.

        // 添加通知观察者

        _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {

        }];

      }     

      - (void)dealloc {

          if (_observer) {

              [[NSNotificationCenter defaultCenter] removeObserver:_observer];

              _observer = nil;

          }

      }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    2)这个方法的block会强持有对象,所以在block中的成员变量和属性,需要弱引用,如果没有弱引用,则需要在delloc函数之前对通知进行移除,否则不会执行delloc函数。

      @property (nonatomic, strong) id observer;

      - (void)viewDidLoad {

        [super viewDidLoad];

        // Do any additional setup after loading the view, typically from a nib.

        // 需要进行弱引用

        __weak typeof(self) weakSelf = self;

        // 添加通知观察者

        _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {

            weakSelf.label.backgroundColor = [UIColor redColor];

        }];

      }     

      - (void)dealloc {

          if (_observer) {

              [[NSNotificationCenter defaultCenter] removeObserver:_observer];

              _observer = nil;

          }

      }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    回调方法

    -(void)notificationFirst:(NSNotification *)notification{

        NSString  *name=[notification name];

        NSString  *object=[notification object];

        NSLog(@"名称:%@----对象:%@",name,object);

    }

    -(void)notificationSecond:(NSNotification *)notification{

        NSString  *name=[notification name];

        NSString  *object=[notification object];

        NSDictionary  *dict=[notification userInfo];

        NSLog(@"名称:%@----对象:%@",name,object);

        NSLog(@"获取的值:%@",[dict objectForKey:@"key"]);

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    二、发送通知的方法

    发送通知主要是以下三种方式:

    - (void)postNotification:(NSNotification *)notification;

    - (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObjectl

    - (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

    1

    2

    3

    注意:发送通知时,所在的线程,如果在子线程发送通知,那么通知的接收也是在子线程。

    发送通知中的object参数和注册时使用的方法关联。

    举个例子:

    // 注册

      self.name = @"Tom";

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test1) name:@"lala" object:_name];

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

    1

    2

    3

    4

    一个object是nil,一个是_name。以下以两种不同的方式去发送通知:

    第一种

    [[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:nil];

    1

    这种方式发送通知的时候,只有test2方法会被调用。

    第二种:

    [[NSNotificationCenter defaultCenter] postNotificationName:@"lala" object:_name];

    1

    这种方法发生通知的时候,test1和test2都会被调用。

    _name = @"lala";

    [[NSNotificationCenter defaultCenter] postNotifcationName:"lala" object:_name];

    1

    2

    此时只有test2被调用,因为_name的地址被更改了,所以通知匹配不上了,导致test1无法被识别到了。以上可以说明如果添加通知的时候传入了object参数,那么发送通知时,就会匹配name和object两个条件。如果没传object参数,则只匹配name。

    移除的时候一样

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"lala" object:_name];

    1

    test1被移除了

    [[NSNotificationCenter defaultCenter] removeObserve: self name:@"lala" object:nil];

    1

    test1和test2都被移除掉了。

    如果使用

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    1

    效果一样,不同之处在于这个方法不止会移除掉自己添加的通知方法,同时也会移除掉系统的通知方法,所以除非是这个对象要被释放掉,不要轻易使用这种方法进行移除通知。

    总结:

    1.如果添加通知的时候传入了object参数,那么发送通知时,就会匹配name和object两个条件。如果没传object参数,则只匹配name。

    2.如果中途修改object对象,则对应有object发送对象的通知方式将会失效。

    使用步骤:

    1.在需要监听的部分注册监听器

    2.实现通知监听器的回调函数

    3.在监听器对象销毁前删除通知监听器

    4.如果有通知需要发送,使用NSNotificationCenter发送通知。

    我们在监听器回调函数中修改UI不会产生任何问题,但当通知是在其他线程中发送的,监听器回调函数中修改UI不会产生任何问题,但当通知是在其他线程中发送的,监听器回调函数很有可能就是在发送通知的那个线程中执行,我们知道UI的更新必须在主线程中执行,这个时候就需要注意,如果通知监听器的回调函数需要更新UI的代码,需要使用GCD放在主线程中执行,代码入下:

    //NextViewController发送通知的代码修改为如下代码:

    - (void)completeButtonClickedHandler

    {

        //使用GCD获取一个非主线程的线程用于发送通知

        dispatch_async(dispatch_get_global_queue(0, 0), ^{

            [[NSNotificationCenter defaultCenter] postNotificationName:@"inputTextValueChangedNotification" object:nil userInfo:@{@"inputText": self.textField.text}];

        });

        [self dismissViewControllerAnimated:YES completion:nil];

    }

    //ViewController通知监听器的回调函数修改为如下代码:

    - (void)inputTextValueChangedNotificationHandler:(NSNotification*)notification

    {

        //使用GCD获取主线程并更新UI

        dispatch_async(dispatch_get_main_queue(), ^{

            self.label.text = notification.userInfo[@"inputText"];

        });

        //如果不在主线程更新UI很有可能无法正确执行

        //self.label.text = notification.userInfo[@"inputText"];

    }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    很多时候我们使用的是第三方框架发送的通知,或是系统提供的通知,我们无法预知这些通知是否是在主线程中发送的,为了安全起见最好在需要更新UI时使用GCD将更新的逻辑放入主线程执行。

    ————————————————

    版权声明:本文为CSDN博主「ACERROR」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

    原文链接:https://blog.csdn.net/qq_36928687/article/details/108857352

    相关文章

      网友评论

          本文标题:NSNotificationCenter的理解和运用 教程【转】

          本文链接:https://www.haomeiwen.com/subject/prsaldtx.html