美文网首页
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