代理、通知、KVO、KVC

2018-07-13  本文已影响12人  蔚尼

代理

代理的工作流程

代理的工作流程

代理的循环引用

代理循环引用

问:为什么使用weak来解决循环引用,不是assign呢?

协议中可以声明属性、方法

iOS Protocol中声明属性
iOS Protocol中声明属性的方法

通知

是通过观察者模式实现跨层传递消息的机制;
传递方式为1对多;

不同之处 代理 通知
设计模式 代理模式实现 观察者模式
传值方式 一对一传值 一对多传值
手动实现通知

KVO

KVO 是key-value observing的缩写。

什么是KVO?(面试)

KVO是对观察者设计模式的又一实现(KVO的实现机制)。
苹果使用了isa混写(isa-swizzling)来实现KVO。

KVO实现细节、原理?(面试)

1.当我们向A添加观察,系统会生成一个NSKVONotfiying_A类继承A;
2.将A的isa指针指向NSKVONotfiying_A;
3.NSKVONotfiying_A类重写观察字段的set方法。如图【重写set方法】,在set方法的前后添加上willChangeValueForKeydidChangeValueForKey方法。当观察的字段改变之后,set方法里面:
1)调用willChangeValueForKey:
2)调用原来的setter实现
3)didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法

KVO的实现原理 重写set方法

isa混写技术在KVO中怎么体现?(面试)

当我们向A添加观察,系统会生成一个NSKVONotfiying_A类继承A;将A的isa指针指向NSKVONotfiying_A,并重写观察字段的set方法。

哪几种赋值方式可以让KVO生效,为什么?(面试)

也就是说,观察类A的属性a,怎么对a进行赋值,可以调用到观察的回调方法。

  1. 通过KVC的方式进行赋值(setValue: forkey:);
  2. 通过.语法触发setter方法进行赋值;
  3. 成员变量直接赋值,并在赋值前后增加willChangeValueForKey、didChangeValueForKey方法。

我们通过下面进行实践:有MObject类,MObserver观察类。通过MObserver观察MObject中value的变化。

MObject类:

.h:
@interface MObject : NSObject
@property (nonatomic, assign) int value;
- (void)increase;
@end

.m:
@implementation MObject

- (id)init
{
    self = [super init];
    if (self) {
        _value = 0;
    }
    return self;
}

@end

MObserver类:进行观察,有新值变化就进行打印

#import "MObject.h"
@implementation MObserver

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if ([object isKindOfClass:[MObject class]] &&
         [keyPath isEqualToString:@"value"]) {
        
        // 获取value的新值
        NSNumber *valueNum = [change valueForKey:NSKeyValueChangeNewKey];
        NSLog(@"value is %@", valueNum);
    }
}

ViewController中进行观察:
通过kvc和点语法触发setter方法,都可以成功收到观察的回调。

    MObject *obj = [[MObject alloc] init];
    MObserver *observer = [[MObserver alloc] init];
    
    //调用kvo方法监听obj的value属性的变化
    [obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:NULL];
   
    // 1. 通过kvc设置value:会调用value的setter方法,所以MObserver能成功收到value值变化的回调
    [obj setValue:@2 forKey:@"value"];

    //2.通过setter方法修改value;直接调用setter方法,可以收到观察的回调
    obj.value = 1;

    // 3. 通过成员变量直接赋值value能否生效?继续看下文。
    [obj increase];

如下,MObject中increase方法直接通过成员方法赋值,没有触发setter方法。MObserver不会收到回调方法。

- (void)increase
{
    //直接为成员变量赋值
    _value += 1;
}

成员变量前后添加上willChangeValueForKey、didChangeValueForKey方法后,MObserver就可以收到回调方法了。

- (void)increase
{
    //直接为成员变量赋值
    [self willChangeValueForKey:@"value"];
    _value += 1;
    [self didChangeValueForKey:@"value"];
}

KVC

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key; 
@interface MJCat : NSObject
@property (assign, nonatomic) int weight;
@end

@interface MJPerson : NSObject
@property (assign, nonatomic) int age;

@property (assign, nonatomic) MJCat *cat;
@end
        //为person的age赋值
        MJPerson *person = [[MJPerson alloc] init];
        [person setValue:@10 forKey:@"age"];
    
      //为person的cat对象里的weight赋值
       person.cat = [[MJCat alloc] init];
       [person setValue:@10 forKeyPath:@"cat.weight"];

KVC设值原理

KVC设值值的流程:


KVC设值原理

KVC设值值可以触发kvo,如下:其实系统默认在setValue:forkey(赋值的)前后调用了willchangValueForKey、didChangeValueForKey


kvc触发kvo本质写法

KVC取值原理

KVC取值原理
上一篇 下一篇

猜你喜欢

热点阅读