链式编程思想和响应式编程思想的学习总结

2016-07-04  本文已影响439人  07212a79db66

1.链式编程思想的代表:Masonry框架

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);
    }];

看到make.left.top.equalTo(@10); 这样的写法,会不会觉得很方便啊. 下面大概分析一下它的执行流程:

下面模仿Masonry实现一个计算器:

创建一个计算管理者类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参数(需要操作的值)

(二) 响应式编程思想

  1. 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的变化.


上一篇 下一篇

猜你喜欢

热点阅读