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