美文网首页
代理、通知、KVO、KVC

代理、通知、KVO、KVC

作者: 蔚尼 | 来源:发表于2018-07-13 11:22 被阅读12次

    代理

    • 代理 准确的说是一种软件设计模式
    • iOS当中一@protocol形式体现
    • 传递方式一对一

    代理的工作流程

    代理的工作流程

    代理的循环引用

    代理循环引用

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

    • weak 表示对对象的弱引用,被weak修饰的对象随时可被系统销毁和回收。
    • 用weak修饰弱引用,不会使传入对象的引用计数加1。当一个对象被销毁时,指针被清空。
    • assigin 可以用非 OC 对象,而 weak 必须用于 OC 对象
    • 当指向的对象释放以后,weak会被自动设置为nil,而assign不会,所以会导致野指针的出现,可能会导致crash

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

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

    • @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
    • @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
    • @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

    通知

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

    不同之处 代理 通知
    设计模式 代理模式实现 观察者模式
    传值方式 一对一传值 一对多传值
    • 通知是同步的还是异步的呢?
      和NSNotificationQueue有关 在不使用这个类的相关作用情况下 也就是通常情况下 是同步的,所谓的同步就是发送通知之后 会阻塞当然线程,直到所有observer处理完成

    • 如何实现通知的机制?
      通知是NS开头的。NS开头的框架无法知道源代码。
      如果要手动实现通知。如下图,我们需要一个map存储所有的通知,每个通知的名称notificationName关联一个列表Observer_list。列表里面的每个Observer存放在调用的方法、传递的参数等。

    手动实现通知

    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

    • 什么是KVC
      Key-value coding的缩写。是一种键值编码技术。
    • 通过键值编码技术,是否违背面向对象的编程思想,是否破坏面向对象的方法呢?
      会破坏。
      Key:是没有任何限制的,在知道某一个类内部私有名称的情况下,可以对私有变量进行访问的。破坏面向对象的思想

    • kvc的api

    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    - (void)setValue:(id)value forKey:(NSString *)key;
    - (id)valueForKeyPath:(NSString *)keyPath;
    - (id)valueForKey:(NSString *)key; 
    
    • kvc使用
    @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取值原理

    相关文章

      网友评论

          本文标题:代理、通知、KVO、KVC

          本文链接:https://www.haomeiwen.com/subject/wgkrpftx.html