1.链式编程思想的代表:Masonry框架
- 主要的特点:链式编程是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好,特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)
2.响应式编程思想的代表:KVO的运用
- 主要特点: 不需要考虑调用的顺序,只需要知道结果
(一) 链式编程思想
下面代码为使用Masonry给一个控件添加约束:
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
// block:把需要操作的值当做block参数,block也需要返回值,就是方法调用者
// 设置约束
// 给make添加left,top约束,调用equalTo给这两个约束赋值
make.left.top.equalTo(@10);
make.right.bottom.equalTo(@-10);
}];
- 注意:如果将[self.view addSubview:redView];这句代码写在布局后面运行程序,会挂.所以给控件添加约束之前应先把控件添加到view上面
看到make.left.top.equalTo(@10); 这样的写法,会不会觉得很方便啊. 下面大概分析一下它的执行流程:
- 1.给所有UIView提供一个设置约束的方法mas_makeConstraints
- 2.mas_makeConstraints方法实现:创建约束制造者,约束制造者提供了很多设置约束的方法,每个制造约束(equalTo)的方法,方法不需要参数,但是必须返回block,每个block需要传入一个参数,用于计算控件的约束。
下面模仿Masonry实现一个计算器:
- 1.给所有类提供一个计算方法dj_makeResult,
- 2.创建计算制造者,计算制造者提供了很多方法计算,每个计算方法,方法不需要参数,但是必须返回block,每个block需要传入一个参数,计算值。
- 3.传入block,block用于把所有的计算保存到计算制造者中
- 4.执行blcok
- 5.把计算制造者计算的值,返回出去
创建一个计算管理者类CalculateManager,Masonry给UIView提供mas_makeConstraints方法设置约束,那暂给NSObject提供一个分类:+ (int)dj_makeResult:(void(^)(CalculateManager *manager))block;
#import <Foundation/Foundation.h>
@class CalculateManager;
@interface NSObject (Calculate)
+ (int)dj_makeResult:(void(^)(CalculateManager *manager))block;
@end
#import "NSObject+Calculate.h"
#import "CalculateManager.h"
@implementation NSObject (Calculate)
+ (int)dj_makeResult:(void(^)(CalculateManager *manager))block {
//创建计算器管理者
CalculateManager *manager = [[CalculateManager alloc] init];
block(manager);
return manager.result;
}
@end
#import <Foundation/Foundation.h>
@interface CalculateManager : NSObject
/** 保存计算结果*/
@property (nonatomic, assign) int result;
- (CalculateManager *(^)(int))add;
- (CalculateManager *(^)(int))sub;
- (CalculateManager *(^)(int))muilt;
- (CalculateManager *(^)(int))divide;
@end
#import "CalculateManager.h"
@implementation CalculateManager
- (CalculateManager *(^)(int))add {
return ^CalculateManager *(int value) {
self.result += value;
return self;
};
}
-(CalculateManager *(^)(int))sub {
return ^CalculateManager *(int value) {
self.result -= value;
return self;
};
}
- (CalculateManager *(^)(int))muilt {
return ^CalculateManager *(int value) {
self.result *= value;
return self;
};
}
- (CalculateManager *(^)(int))divide {
return ^CalculateManager *(int value) {
self.result /= value;
return self;
};
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
int result = [NSObject dj_makeResult:^(CalculateManager *manager) {
manager.add(10).divide(10);
}];
NSLog(@"%d",result);
}
总结:链式编程是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好,特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)
(二) 响应式编程思想
- KVO的简单说明
KVO全称是Key-value observing,当一个类的某个属性值发生变化的时候(比如数据类),需要另外一个类的某个属性(比如视图类)的某个属性做出相应的变化,这个时候我们就运用KVO来实现这样的需求.首先要注册监听,监听某个类里面属性的值,然后重写- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context方法,在这个方法里面做一些操作.
2.键值观察是如何实现的:
下面通过一个例子说明
新建一个Person类,提供一个属性name
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
@public
NSString *_name; //便于外面访问成员变量
}
@property (nonatomic, strong) NSString *name;
@end
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
_person = p;
[p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
NSLog(@"person的name值改变了:%@",_person.name);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//_person.name = @"张三";
_person -> _name = @"张三";
}
@end
当这样写的时候 _person -> _name = @"张三";并没有输出.
修改为:_person.name = @"张三"; 此时控制台有打印信息,监听到name值得变化.
结论:如果仅是直接修改属性对应的成员变量是无法实现KVO的,而_person.name这样写会调用name的setter方法.
运行程序,继续
观察此时p对象的isa指针指向的并不是person类,而是系统自定义的 NSKVONotifying_Person子类
总结:KVO的本质就是监听一个对象有没有调用set方法,它的实现原理就是:当某个类对象被观察时,系统会在运行期动态的创建该类的一个派生类(子类),然后在子类中重写父类中任何被观察属性的 setter 方法。子类在被重写的 setter 方法时实现真正的通知机制.正如前面看到的那样,直接修改属性对应的成员变量并不会调用setter方法,所以无法实现KVO.与此同时系统将这个对象的isa指针指向子类,因此p对象就成为子类的对象了,重写setName,在内部恢复父类做法,通知观察者.
通过以上的了解,下面自定义KVO的实现,对person的name进行监听:
1 > 首先自定义NSKVONotifying_Person子类
2 > 重写setName,在内部恢复父类做法,通知观察者
3 > 如何让外界调用自定义Person类的子类方法,修改当前对象的isa指针,指向NSKVONotifying_Person
第一步:给NSObject新增一个分类:
#import <Foundation/Foundation.h>
@interface NSObject (KVO)
- (void)dj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end
#import "NSObject+KVO.h"
#import "objc/message.h"
#import "DJKVONotifying_Person.h"
NSString *const observerKey = @"observer";
@implementation NSObject (KVO)
- (void)dj_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
//外界调用自定义Person类的子类方法,通过运行时修改当前对象的isa,指向DJKVONotifying_Person
// 把观察者保存到当前对象
objc_setAssociatedObject(self, (__bridge const void *)(observerKey), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 修改对象isa指针
object_setClass(self, [DJKVONotifying_Person class]);
}
@end
#import "DJKVONotifying_Person.h"
#import <objc/message.h>
extern NSString *const observerKey;
@implementation DJKVONotifying_Person
- (void)setName:(NSString *)name {
[super setName:name];
// 通知观察者调用observeValueForKeyPath
// 需要把观察者保存到当前对象
// 获取观察者
id obsetver = objc_getAssociatedObject(self, observerKey);
[obsetver observeValueForKeyPath:@"name" ofObject:self change:nil context:nil];
}
@end
运行程序:
运行程序,可以看到isa指针指向了自定义的DJKVONotifying_Person类,并且也能监听name的变化.
网友评论