美文网首页
iOS代理,通知,KVO

iOS代理,通知,KVO

作者: 炒河粉儿 | 来源:发表于2019-08-03 15:48 被阅读0次

    代理

    有 A B
    A中声明代理 B中实现代理

    在A.h文件中
    在A的@interface 上面声明代理

    @protocol  TestDelegate <NSObject>
    
    //必须实现的方法
    @required
    
    - (void)eat:(NSString *)foodName;
    
    //可选择实现的方法
    @optional
    
    - (void)study:(NSString *)className;
    
    @end
    
    //创建代理对象
    @property (nonatomic, weak) id <TestDelegate> delegate;
    
    在A.m 文件中实现委托
    先判断代理对象是否实现了这个协议方法,实现了再调用
     if ([self.delegate respondsToSelector:@selector(eat:)])
        {
            // 代理去调用实现的代理方法
              [self.delegate eat:@"火龙果"];
        }
    
    

    在B中引用
    在B.h文件中

    #import "A.h"
    @interFace B:UIViewController<TestDelagate>
    
    //在B.m中
    A =[ [A allco]init];
    A.delegate = self;
    
    //实现代理方法
    - (void)eat:(NSString *)foodName
    {
    NSLog:(@"食物名称:%@",foodName);
    }
    

    代理为什么要用weak声明

    用strong 声明为强引用 retain 引用计数加1 造成循环引用 weak则是弱引用

    A弱引用delegate 签署后delegate即为B B强引用A 此时不回造成循环引用 会dealloc

    如果使用strong 则A强引用B B强引用A 互相持有 无法释放 造成内存泄漏

    通知

    NSNotification 的3种使用方式

    • 不传递参数,最常用的一种
    //发送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"notifyName1" object:nil];
    //接收通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification1) name:@"notifyName1" object:nil];
    //实现方法
    -(void)notification1{
    NSLog(@"接收 不带参数的消息");
    }
    
    • 使用object传递消息
    //发送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"notifyName2" object:[NSString stringWithFormat:@"%@",btn.titleLabel.text]];
    //接收通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification2:) name:@"notifyName2" object:nil];
    //实现方法
    -(void)notification2:(NSNotification *)noti{
    
    //使用object处理消息
    NSString *info = [noti object];
    NSLog(@"接收 object传递的消息:%@",info);
    
    }
    
    
    • 使用userInfo传递消息
    //发送通知
    NSDictionary *dic = [NSDictionary dictionaryWithObject:@"userInfo消息" forKey:@"param"];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"nitifyName3" object:nil userInfo:dic];
    //接收通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification3:) name:@"nitifyName3" object:nil];
    //实现方法
    -(void)notification3:(NSNotification *)noti{
    
    //使用userInfo处理消息
    NSDictionary  *dic = [noti userInfo];
    NSString *info = [dic objectForKey:@"param"];
    NSLog(@"接收 userInfo传递的消息:%@",info);
    
    }
    
    
    

    在接收消息的页面,在dealloc方法里面移除观察者。

    -(void)dealloc{
    
    //移除观察者
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    

    在一个大的项目中, 由于通知较多, 一般建立一个文件统一管理通知名, 通知名定义为常量

    .h
    #import <Foundation/Foundation.h>
    
    // extern : 引用本类或者其他类中使用
    // xxA类通知
    extern NSString *const NotifyName1;
    extern NSString *const NotifyName2;
    
    // xxB类通知
    extern NSString *const xxx
    extern NSString *const xxx
    
    //xxC类通知
    extern NSString *const xxx;
    
    .m
    /**
     常量值
     .m 文件中进行赋值  const:标识后面的东西是不可修改的
     */
    
    // xxA类通知
    NSString *const NotifyName1 = @"NotifyName1";
    NSString *const NotifyName2 = @"NotifyName2";
    
    // xxB类通知
    NSString *const xxx = ...'
    NSString *const xxx = ....
    

    发送通知在哪个线程 ,接受执行方法就在哪个线程。

    在多线程应用中,Notification在哪个线程中post,就在哪个线程中被转发,而不一定是在注册观察者的那个线程中。
    也就是说,Notification的发送与接收处理都是在同一个线程中。为了说明这一点,我们先来看一个示例:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        NSLog(@"current thread = %@", [NSThread currentThread]);
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:TEST_NOTIFICATION object:nil];
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
            [[NSNotificationCenter defaultCenter] postNotificationName:TEST_NOTIFICATION object:nil userInfo:nil];
        });
    }
    
    - (void)handleNotification:(NSNotification *)notification
    {
        NSLog(@"current thread = %@", [NSThread currentThread]);
    
        NSLog(@"test notification");
    }
    
    @end
    其输出结果如下:
    2015-03-11 22:05:12.856 test[865:45102] current thread = {number = 1, name = main}
    2015-03-11 22:05:12.857 test[865:45174] current thread = {number = 2, name = (null)}
    2015-03-11 22:05:12.857 test[865:45174] test notification
    

    可以看到,虽然我们在主线程中注册了通知的观察者,但在全局队列中post的Notification,并不是在主线程处理的。所以,这时候就需要注意,如果我们想在回调中处理与UI相关的操作,需要确保是在主线程中执行回调。

    post是在子线程 因此 接受处理也是在子线程 delloc也是在子线程

    有种情况是 发送方和接收方不在一个线程 如果接收方没被dealloc就没问题。 假设接收方observer已经被释放,然后再post,那么就会EXC_BAD_ACCESS

    那我们该怎么做呢?这里有一些好的建议:

    1. 尽量在一个线程中处理通知相关的操作,大部分情况下,这样做都能确保通知的正常工作。不过,我们无法确定到底会在哪个线程中调用dealloc方法,所以这一点还是比较困难。
    2. 注册监听都时,使用基于block的API。这样我们在block还要继续调用self的属性或方法,就可以通过weak-strong的方式来处理。具体大家可以改造下上面的代码试试是什么效果。
    3. 使用带有安全生命周期的对象,这一点对象单例对象来说再合适不过了,在应用的整个生命周期都不会被释放。
    4. 使用代理。

    KVO

    KVO 观察者模式
    键值观察Key-Value-Observer就是观察者模式

    //注册
    //keyPath就是要观察的属性值
    //options给你观察键值变化的选择
    //context方便传输你需要的数据
    -(void)addObserver:(NSObject *)anObserver 
            forKeyPath:(NSString *)keyPath 
               options:(NSKeyValueObservingOptions)options 
               context:(void *)context
    //实现
    //change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。
    -(void)observeValueForKeyPath:(NSString *)keyPath 
                         ofObject:(id)object
                           change:(NSDictionary *)change 
                          context:(void *)context
    //移除
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
    

    当类A的对象被观察的时候,系统会动态创建一个A的派生类B,B重写A的set方法,并实现通知机制。B会重新class方法,伪装成A系统将类A对象的isa指针指向B。

    isa_swizzling 本质时runtime

    相关文章

      网友评论

          本文标题:iOS代理,通知,KVO

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