KVO只能监听属性的变化,通过NSString类型的属性名称来实现。实现了自动监听,当属性变化时,会自动通知观察者,不用再添加代码了。但是观察者得持有被观察者的引用,以便被观察者注册观察者,耦合性太高,不利于代码的维护。
NSNotification表灵活,可以监听的内容也不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。但是需要被观察者手动发送通知,观察者监听后才能才能响应,比KVO多了发送通知的一步。但是观察者注册监听者不需要被观察者的引用,没有耦合性,利于代码的维护。
通知是一种发送给一个或者多个观察者,用来通知其在程序中发生了某个事件的消息。 通知机制的核心就是一个进程中单一实例的对象,被叫做通知中心(NSNotificationCenter)。 自定义通知的步骤:
注册监听者创建并发送通知移除监听者首先初始化一个通知对象:
/*NSNotification 通知 通过学习KVO,我们发现KVO是一种简单的观察者设计模式,涉及到两个对象,分别是观察者和被观察者。这种方式实质有很大的局限性,那么OC的‘Foundation’框架又为开发者提供了新的一种观察者模式,即通知。 通知,是一种发送给一个或多个观察者,用来通知其在程序中发生了某个事件的消息。Cocoa的通知机制遵循的是一种广播模式。它是一种程序中时间的发起者或者是处理和其他想要知道该事件的对象沟通的一种方式。消息的接受者,也就是观察者响应该事件来改变自己的UI、行为或者状态。 在OC中,使用NSNotification类来表示一个通知。 */ /* name:通知名称 object:通知发起者 userInfo:表示通知内容 */ //初始化一个通知(NSNotification)的实例对象 NSNotification *notification1 = [NSNotification notificationWithName:@"通知名称1" object:self]; NSNotification *notification2 = [NSNotification notificationWithName:@"通知名称2" object:self userInfo:@{@"content":@"Hello world!"}];创建通知中心:
//创建通知中心 是一个单例类,拿到通知中心的单例 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; /* 建立通知的发送机制,如下: 1.注册相关的监听者,并且实现在需要的时候回调方法 2.在需要的时候,被监听者的对象去到通知中心发送消息 3.在‘dealloc’方法中,移除通知 */创建一个Student类(注册监听者+实现回调方法+移除通知)
-(id)init{ if (self = [super init]) { //1.注册监听者 //通知中心是单例的,与center是同一个 /* 参数的含义如下: 1.要去接收通知的对象 2.接收通知回调的相应方法 3.接收通知的名字 4.发起通知的对象,一般我们不需要知道,把它写成nil */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationAction:) name:@"通知名称2" object:nil]; } return self; } //监听到通知后回调的方法 -(void)notificationAction:(NSNotification *)notification{ NSLog(@"%@",notification.userInfo); } //移除某个通知的监听者 - (void)dealloc{ NSLog(@"移除了吗?"); [[NSNotificationCenter defaultCenter] removeObserver:self name:@"通知名称2" object:nil]; //[[NSNotificationCenter defaultCenter] removeObserver:self];移除所有的监听者 }发送通知
//接收通知的对象 Student *student = [[Student alloc] init]; //发送通知 [center postNotification:notification2]; //其他的发送通知的方式 // [center postNotificationName:@"通知3" object:self]; // [center postNotificationName:@"通知4" object:self userInfo:@{}];运行结果为:
2016-08-16 17:40:32.263 OC_08_03[1406:153231] { content = "Hello world!"; 2016-08-16 17:40:32.264 OC_08_03[1406:153231] 移除了吗?再来一个例子,创建一个 Weather 类和 PhoneUser 类,PhoneUser注册成为监听者,Weather发送通知到通知中心,PhoneUser 做出响应。
Weather 类有一个发送消息的方法:
- (void)sendMessage{ //发送通知 [[NSNotificationCenter defaultCenter] postNotificationName:WeatherAndPhoneUser object:self userInfo:@{@"weather":@"Sunny"}];PhoneUser 类中的一些操作:
- (id)init{ if (self = [super init]) { //注册成为监听者 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationAction:) name:@"WeatherAndPhoneUser" object:nil]; } return self; } //notificationAction:方法的实现 - (void)notificationAction:(NSNotification *)notification{ NSDictionary *dic = notification.userInfo; NSLog(@"%@",dic); } //移除监听者 - (void)dealloc{ NSLog(@"难道没有移除?"); [[NSNotificationCenter defaultCenter] removeObserver:self]; }返回主函数中,创建Weather对象和PhoneUser对象,并发送通知:
Weather *weather = [[Weather alloc] init]; PhoneUser *phoneUser = [[PhoneUser alloc] init]; [weather sendMessage];运行结果为:
2016-08-16 17:42:07.250 OC_08_03[1414:154430] { weather = Sunny; } 2016-08-16 17:42:07.250 OC_08_03[1414:154430] 难道没有移除? //这是一个方法,表示对iPhone模拟器进行了点击操作才会执行 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ NSLog(@"你点了我"); }在某些情况下,需要对一些现有的类添加一些新的方法。通常的做法是继承要添加方法的类,然后在子类中扩展出新的方法。但有些情况会不适合这种方式去添加新的方法。这时可以利用Objective-C的动态语言机制,可以为现有的类添加新的方法,这种技术叫作“类目(Category)”。
Category:
定义:为现有工程中的类和系统类添加新的方法。作用:在类目中添加新的方法及属性和在原类中添加方法和属性是一样的,都能够被类和类的子类调用,这样可以快速便利维护和开发项目。创建一个Tool类:
Tool.h文件中
@interface Tool : NSObject { NSString *_name; } @property (nonatomic,strong)NSString *property; //原始类方法 - (void)function1; @end #pragma mark --类目-- //在类目中为原类添加新的方法和属性 /* 类目的使用场景 1.类包含了很多个方法实现,而这些方法需要不同团队成员来实现 2.当你在使用基础类库中的类时,你不想继承这些类而指向添加一些方法时 使用类目的时候需要注意的问题 1.类目可以访问原始类的实例变量,但不能添加实例变量,如果想添加实例变量,那就通过继承创建子类来实现。 2.类目可以重载原始类的方法,但不推荐这么做,这样会覆盖掉原始类的方法。如果确实要重载,那就通过继承创建子类来实现。 3.和普通接口有所区别的是,在类目的实现文件中的实例方法只要你不去调用它你可以不用实现所有声明的所有方法。 */ @interface Tool (ToolCategory) //{ 类目中不能添加实例变量 // NSString *_property2; //} /* iOS运行时机制,简单来说,就是苹果为开发者提供的一套运行时动态创建类、方法、属性的API,它是一套纯C语言的API。 */ @property (nonatomic,strong)NSString *property2; //- (void)function1; - (void)function2;Tool.m文件中
#import "Tool.h" //为了在类目中创建设置器和访问器 #import <objc/runtime.h> @implementation Tool - (void)function1{ NSLog(@"I am function1"); } @end //这是一个类目 @implementation Tool (ToolCategory) - (void)function2{ NSLog(@"%@",self.property2);//缺少设置器与访问器 NSLog(@"%@",_name);//类目可以访问实例变量 NSLog(@"I am function2"); } #pragma mark --合成类目中的属性-- //一般设置器与访问器的写法不可用 - (void)setProperty2:(NSString *)property2{ /* <#id object#>:给哪个对象的属性赋值 <#const void *key#>:属性对应的key <#id value#>:设置属性的值为value <#objc_AssociationPolicy policy#>:使用的策略 */ objc_setAssociatedObject(self, @selector(property2),property2,OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)property2{ return objc_getAssociatedObject(self, @selector(property2)); } //结果没有property2?->没有赋值 //- (void)function1{ // // NSLog(@"我是类目中的function1"); //}会覆盖原始类的方法,建议通过创建子类继承原始类的方式重写方法主文件中:
Tool *tool = [Tool new]; [tool function1]; tool.property2 = @"property2"; [tool setValue:@"Rick" forKey:@"_name"]; [tool function2];也可以新建一个类目文件:
@interface Tool (Tool_Category) - (void)function3; @implementation Tool (Tool_Category) - (void)function3{ NSLog(@"I am function3"); }为NSObject类创建一个类目,但是不去实现
@interface NSObject (NSObject_Category) //创建NSObject的类目而不实现称为‘创建一个非正式协议’ - (void)test1; - (void)test2;延展的作用就是定义自己的私有方法。
创建一个新的MyClass类
//#import "MyClass_MyClass_Extension.h"不能在.h文件中导入延展文件,不然会报错,因为延展定义的都是私有方法,在.h文件中的可以被外部调用。 //为MyClass添加延展 @interface MyClass () - (void)testExtension; @end //延展可以在原@implementation中直接添加实现 @implementation MyClass - (void)test1{ NSLog(@"test1"); } - (void)test2{ NSLog(@"test2"); [self testExtension];//可以在内部调用 } //私有方法,不能在外部调用 - (void)testExtension{ NSLog(@"I am testExtension"); } - (void)testExtension2{ NSLog(@"I am testExtension"); }新建一个延展文件
#import "MyClass.h" //创建的延展文件只有一个.h文件,实现在原类的.m文件中 @interface MyClass () - (void)testExtension2;