详解苹果的黑魔法 – KVO 的奥秘

在iOS开发中,苹果提供了许多机制给我们进行回调。KVO(key-value-observing)是一种十分有趣的回调机制,在某个对象注册监听者后,在被监听的对象发生改变时,对象会发送一个通知给监听者,以便监听者执行回调操作。最常见的KVO运用是监听scrollView的contentOffset属性,来完成用户滚动时动态改变某些控件的属性实现效果,包括渐变导航栏、下拉刷新控件等效果。
http://jbcdn2.b0.upaiyun.com/2015/12/36a1f0f0ac4af124dbdb65895b2d0fc9.gif

使用

KVO的使用非常简单,使用KVO的要求是对象必须能支持kvc机制——所有NSObject的子类都支持这个机制。拿上面的渐变导航栏做,我们为tableView添加了一个监听者controller,在我们滑动列表的时候,会计算当前列表的滚动偏移量,然后改变导航栏的背景色透明度。

//添加监听者
[self.tableView addObserver: self forKeyPath: @"contentOffset" options: NSKeyValueObservingOptionNew context: nil];
/**

  • 监听属性值发生改变时回调
    */
  • (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
    CGFloat offset = self.tableView.contentOffset.y;
    CGFloat delta = offset / 64.f + 1.f;
    delta = MAX(0, delta);
    [self alphaNavController].barAlpha = MIN(1, delta);
    }

毫无疑问,kvo是一种非常便捷的回调方式,但是编译器是怎么完成监听这个任务的呢?先来看看苹果文档对于KVO的实现描述

Automatic key-value observing is implemented using a technique called isa-swizzling… When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class ..
简要的来说,在我们对某个对象完成监听的注册后,编译器会修改监听对象(上文中的tableView)的isa指针,让这个指针指向一个新生成的中间类。从某个意义上来说,这是一场骗局。

typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id

KVO使用三步走:

(1)注册成为观察者

(2)观察者定义KVO的回调

(3)移除观察者

来点实际的,还得上代码:

KVOClass接口:

import

@interface KVOClass : NSObject
@property(strong,nonatomic) NSString * name;
@end


KVOClass实现
#import "KVOClass.h"

@implementation KVOClass
//在init注册观察者
-(id) init
{
if (self = [super init]) {
[self addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:@"name"];
}
return self;
}

//重写观察方

  • (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
    if (context == @"name") {
    NSLog(@"name被改变啦!");
    } else {
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
    }
    //移除观察者
    -(void) dealloc
    {
    [self removeObserver:self forKeyPath:@"name"];
    [super dealloc];
    }
    @end

待续。。。。。。。。。。。。。。

你可能感兴趣的:(详解苹果的黑魔法 – KVO 的奥秘)