通信方式

Delegate

delegate 是委托模式,委托模式是将一件属于委托者做的事情,交给另外一个被委托者来处理。

一个标准的委托由以下的部分组成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@protocol FeederDelegate <NSObject>
- (void)feed;
@end

@interface People : NSObject<FeederDelegate>
@end

@interface Dog : NSObject
@property(nonatomic, weak) id<FeederDelegate> delegate;
@end

也就是说,People 是一个必须要满足 Feeder 这个协议的模型,那么它必须实现一个 feed 函数。在 Dog 这个对象创建的时候可以将其 delegate 成员:

1
2
3
4
5
6
7
- (People *)createPeople {
    People *people = [[People alloc] init];
    Dog *dog = [[Dog alloc] init];
    dog.delegate = people;			// 赋值 delegate 属性
    people.dog = dog;
    return people;
}

比如我们需要捕获对于一个按钮相应的动作事件,我们可以通过下面的两步完成:

  • 在创建 Button 时,将 UIControlEventTouchUpInside 这个对象绑定一个名为 buttonHandler 的函数:

    1
    2
    3
    4
    
    - (void)createButton {
        UIButton *button = [[UIButton alloc] init];
        [button addTarget:self action:@selector(buttonHandler:) forControlEvents:UIControlEventTouchUpInside];
    }
    
  • 然后 buttonHandler 则可以在我们自己的类中实现:

    1
    2
    3
    
    - (void)buttonHandler:(UIButton *)button {
        // 点击按钮
    }
    

Block

可以通过 typedef 定义一个 Block 类型,然后类似地实现。比如下面是定义和实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
typedef void (^FeedHandler)(void);

@class People;
@interface Dog : NSObject
@property (nonatomic, copy) FeedHandler handler;
@end

@implementation Dog
- (void)hungry {
    if (self.handler != nil) {
        self.handler();
    }
}
@end

然后可以用下面的方式给 Dog 的 handler 赋值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@implementation People

- (void)setDog:(Dog *)dog {
    if (_dog != dog) { // 判断不同的dog
        _dog = dog;
        __weak __typeof(self)weakSelf = self;
        _dog.handler = ^{
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            [strongSelf cook];
            [strongSelf feedPet];
            [strongSelf clear];
        };
    }
}

@end

NSNotification

大部分情况下,前两者实现方式能解决大部分问题,但是有一些情况是无法解决的,比如:

  • 一对多。依赖关键远(比如两个页面间的通信)

于是需要用到 NSNotification 这样一个通知系统,这个通知系统的使用主要有以下三步:

  • 添加通知:

    1
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationFirst:) name:@"people.name" object:nil];
    
  • 上面的 notificationFirst 就是一个类自己实现的回调方法:

    1
    2
    3
    
    - (void)notificationFirst:(NSNotification *)notification {
        NSLog(@"people.name:%@", notification.object); // people.name:小王
    }
    
  • 在其他地方发送通知,则这个类则会收到相应:

    1
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"people.name" object:@"小王"];
    

KVC && KVO

什么是 KVC?

  • KVC:Key-Value Coding,即键值编码。

  • 它是一种不通过存取方法,而通过属性名称字符串间接访问属性的机制。

  • 取值和赋值实现了下面的两个函数:

    1
    2
    
    -(void)setValue:(nullable id)value forKey:(NSString *)key;
    -(nullable id)valueForKey:(NSString *)key;
    
  • 除此之外,KVC 也提供了传入路径进行访问的方法:

  • 1
    2
    
    -(nullable id)valueForKeyPath:(NSString *)keyPath;
    -(void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
    

    比如传入 student.name,就表示 student 属性里的 name 属性

什么是 KVO?

  • Key-Value Obersver,即键值观察。它是观察者模式的一种衍生。
  • 基本思想是,对目标对象的某属性添加观察,当该属性发生变化时,会自动的通知观察者。这里所谓的通知是触发观察者对象实现的 KVO 的接口方法。

下面讲一个 KVO 的例子:

  • 首先给目标对象的属性添加观察,下面是这个方法的原型:

    1
    
    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    

    其中,NSKeyValueObservingOptions 是一个枚举常量:

    1
    2
    3
    4
    5
    
    NSKeyValueObservingOptions:
    	NSKeyValueObservingOptionNew // 提供更改前的值
    	NSKeyValueObservingOptionOld // 提供更改后的值
    	NSKeyValueObservingOptionInitial // 观察最初的值(在注册观察服务时会调用一次触发方法)
    	NSKeyValueObservingOptionPrior // 分别在值修改前后触发方法(即一次修改有两次触发)
    
  • 实现下面方法来接收通知,需要注意各个参数的含义:

    1
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
    
  • 最后要移除观察者:

    1
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context