iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车

时间:2022-08-30 14:47:54

一、通知


1、通知中心(NSNotificationCenter)
每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信
任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么。其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车

2、通知(NSNotification)
一个完整的通知一般包含3个属性:
- (NSString *)name; // 通知的名称
- (id)object; // 通知发布者(是谁要发布通知)
- (NSDictionary *)userInfo; // 一些额外的信息(通知发布者传递给通知接收者的信息内容)

初始化一个通知(NSNotification)对象
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;

3、发布通知
通知中心(NSNotificationCenter)提供了相应的方法来帮助发布通知
- (void)postNotification:(NSNotification *)notification;
发布一个notification通知,可在notification对象中设置通知的名称、通知发布者、额外信息等

- (void)postNotificationName:(NSString *)aName object:(id)anObject;
发布一个名称为aName的通知,anObject为这个通知的发布者

- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
发布一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为额外信息

4、注册通知监听器
通知中心(NSNotificationCenter)提供了方法来注册一个监听通知的监听器(Observer)
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
observer:监听器,即谁要接收这个通知
aSelector:收到通知后,回调监听器的这个方法,并且把通知对象当做参数传入
aName:通知的名称。如果为nil,那么无论通知的名称是什么,监听器都能收到这个通知
anObject:通知发布者。如果为anObject和aName都为nil,监听器都收到所有的通知

- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;
name:通知的名称
obj:通知发布者
block:收到对应的通知时,会回调这个block
queue:决定了block在哪个操作队列中执行,如果传nil,默认在当前操作队列中同步执行

5、取消注册通知监听器
通知中心不会保留(retain)监听器对象,在通知中心注册过的对象,必须在该对象释放前取消注册。否则,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息。因为相应的监听器对象已经被释放了,所以可能会导致应用崩溃

通知中心提供了相应的方法来取消注册监听器
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;

一般在监听器销毁之前取消注册(如在监听器中加入下列代码):

- (void)dealloc {
//[super dealloc]; 非ARC中需要调用此句
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

 

6、UIDevice通知
UIDevice类提供了一个单粒对象,它代表着设备,通过它可以获得一些设备相关的信息,比如电池电量值(batteryLevel)、电池状态(batteryState)、设备的类型(model,比如iPod、iPhone等)、设备的系统(systemVersion)

通过[UIDevice currentDevice]可以获取这个单粒对象

UIDevice对象会不间断地发布一些通知,下列是UIDevice对象所发布通知的名称常量:
UIDeviceOrientationDidChangeNotification // 设备旋转
UIDeviceBatteryStateDidChangeNotification // 电池状态改变
UIDeviceBatteryLevelDidChangeNotification // 电池电量改变
UIDeviceProximityStateDidChangeNotification // 近距离传感器(比如设备贴近了使用者的脸部)

7、键盘通知
我们经常需要在键盘弹出或者隐藏的时候做一些特定的操作,因此需要监听键盘的状态

键盘状态改变的时候,系统会发出一些特定的通知
UIKeyboardWillShowNotification // 键盘即将显示
UIKeyboardDidShowNotification // 键盘显示完毕
UIKeyboardWillHideNotification // 键盘即将隐藏
UIKeyboardDidHideNotification // 键盘隐藏完毕
UIKeyboardWillChangeFrameNotification // 键盘的位置尺寸即将发生改变
UIKeyboardDidChangeFrameNotification // 键盘的位置尺寸改变完毕

系统发出键盘通知时,会附带一下跟键盘有关的额外信息(字典),字典常见的key如下:
UIKeyboardFrameBeginUserInfoKey // 键盘刚开始的frame
UIKeyboardFrameEndUserInfoKey // 键盘最终的frame(动画执行完毕后)
UIKeyboardAnimationDurationUserInfoKey // 键盘动画的时间
UIKeyboardAnimationCurveUserInfoKey // 键盘动画的执行节奏(快慢)

 

下面通过购物车案例了解通知、代理以及kvo的使用

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车

 

二、通知


cell内部:

   // 发布通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"plusClickNotification" object:self];
    // 发布通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"minusClickNotification" object:self];

控制器中:

// 注册通知监听器
      NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(plusClick:) name:@"plusClickNotification" object:nil];
    [center addObserver:self selector:@selector(minusClick:) name:@"minusClickNotification" object:nil];
    
    // 监听通知
- (void)plusClick:(NSNotification *)note
{
    self.buyButton.enabled = YES;
    
    // 取出cell(通知的发布者)
    XMGWineCell *cell = note.object;
    // 计算总价
    int totalPrice = self.totalPriceLabel.text.intValue + cell.wine.money.intValue;
    // 设置总价
    self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
}

- (void)minusClick:(NSNotification *)note
{
    // 取出cell(通知的发布者)
    XMGWineCell *cell = note.object;
    // 计算总价
    int totalPrice = self.totalPriceLabel.text.intValue - cell.wine.money.intValue;
    // 设置总价
    self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
    
    self.buyButton.enabled = totalPrice > 0;
}

全代码:

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车
  1 /****************** XMGWine*********************/
  2 #import <Foundation/Foundation.h>
  3 
  4 @interface XMGWine : NSObject
  5 @property (copy, nonatomic) NSString *money;
  6 @property (copy, nonatomic) NSString *name;
  7 @property (copy, nonatomic) NSString *image;
  8 
  9 /** 数量 */
 10 @property (nonatomic, assign) int count;
 11 @end
 12 
 13 #import "XMGWine.h"
 14 
 15 @implementation XMGWine
 16 
 17 @end
 18 
 19 /***************** XMGCircleButton*****************/
 20 #import <UIKit/UIKit.h>
 21 
 22 @interface XMGCircleButton : UIButton
 23 
 24 @end
 25 
 26 #import "XMGCircleButton.h"
 27 
 28 @implementation XMGCircleButton
 29 
 30 - (void)awakeFromNib
 31 {
 32     // 设置边框颜色
 33     self.layer.borderColor = [UIColor redColor].CGColor;
 34     // 设置边框宽度
 35     self.layer.borderWidth = 1;
 36     // 设置圆角半径
 37     self.layer.cornerRadius = self.frame.size.width * 0.5;
 38 }
 39 
 40 @end
 41 
 42 /****************** XMGWineCell*********************/
 43 #import <UIKit/UIKit.h>
 44 @class XMGWine, XMGCircleButton;
 45 
 46 @interface XMGWineCell : UITableViewCell
 47 @property (strong, nonatomic) XMGWine *wine;
 48 @end
 49 
 50 #import "XMGWineCell.h"
 51 #import "XMGWine.h"
 52 #import "XMGCircleButton.h"
 53 
 54 @interface XMGWineCell()
 55 @property (weak, nonatomic) IBOutlet XMGCircleButton *minusButton;
 56 @property (weak, nonatomic) IBOutlet UIImageView *imageImageView;
 57 @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
 58 @property (weak, nonatomic) IBOutlet UILabel *moneyLabel;
 59 @property (weak, nonatomic) IBOutlet UILabel *countLabel;
 60 @end
 61 
 62 @implementation XMGWineCell
 63 
 64 - (void)setWine:(XMGWine *)wine
 65 {
 66     _wine = wine;
 67     
 68     self.imageImageView.image = [UIImage imageNamed:wine.image];
 69     self.nameLabel.text = wine.name;
 70     self.moneyLabel.text = wine.money;
 71     self.countLabel.text = [NSString stringWithFormat:@"%d", wine.count];
 72     self.minusButton.enabled = (wine.count > 0);
 73 }
 74 
 75 /**
 76  *  加号点击
 77  */
 78 - (IBAction)plusClick {
 79     // 修改模型
 80     self.wine.count++;
 81     
 82     // 修改数量label
 83     self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count];
 84     
 85     // 减号能点击
 86     self.minusButton.enabled = YES;
 87     
 88     // 发布通知
 89     [[NSNotificationCenter defaultCenter] postNotificationName:@"plusClickNotification" object:self];
 90 }
 91 
 92 /**
 93  *  减号点击
 94  */
 95 - (IBAction)minusClick {
 96     // 修改模型
 97     self.wine.count--;
 98     
 99     // 修改数量label
100     self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count];
101     
102     // 减号按钮不能点击
103     if (self.wine.count == 0) {
104         self.minusButton.enabled = NO;
105     }
106     
107     // 发布通知
108     [[NSNotificationCenter defaultCenter] postNotificationName:@"minusClickNotification" object:self];
109 }
110 @end
111 
112 /****************** ViewController*********************/
113 #import <UIKit/UIKit.h>
114 
115 @interface ViewController : UIViewController
116 @end
117 
118 #import "ViewController.h"
119 #import "XMGWineCell.h"
120 #import "XMGWine.h"
121 #import "MJExtension.h"
122 
123 @interface ViewController () <UITableViewDataSource>
124 @property (weak, nonatomic) IBOutlet UIButton *buyButton;
125 @property (weak, nonatomic) IBOutlet UITableView *tableView;
126 /** 酒数据 */
127 @property (nonatomic, strong) NSArray *wineArray;
128 /** 总价 */
129 @property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel;
130 @end
131 
132 @implementation ViewController
133 - (NSArray *)wineArray
134 {
135     if (!_wineArray) {
136         self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"];
137     }
138     return _wineArray;
139 }
140 
141 - (void)viewDidLoad {
142     [super viewDidLoad];
143     
144     // 监听通知
145     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
146     [center addObserver:self selector:@selector(plusClick:) name:@"plusClickNotification" object:nil];
147     [center addObserver:self selector:@selector(minusClick:) name:@"minusClickNotification" object:nil];
148 }
149 
150 - (void)dealloc
151 {
152     [[NSNotificationCenter defaultCenter] removeObserver:self];
153 }
154 
155 #pragma mark - 按钮点击
156 - (IBAction)clear {
157     self.totalPriceLabel.text = @"0";
158     
159     // 将模型里面的count清零
160     for (XMGWine *wine in self.wineArray) {
161         wine.count = 0;
162     }
163     
164     // 刷新
165     [self.tableView reloadData];
166     
167     self.buyButton.enabled = NO;
168 }
169 
170 - (IBAction)buy {
171     // 打印出所有要买的东西
172     for (XMGWine *wine in self.wineArray) {
173         if (wine.count) {
174             NSLog(@"%d件【%@】", wine.count, wine.name);
175         }
176     }
177 }
178 
179 #pragma mark - 监听通知
180 - (void)plusClick:(NSNotification *)note
181 {
182     self.buyButton.enabled = YES;
183     
184     // 取出cell(通知的发布者)
185     XMGWineCell *cell = note.object;
186     // 计算总价
187     int totalPrice = self.totalPriceLabel.text.intValue + cell.wine.money.intValue;
188     // 设置总价
189     self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
190 }
191 
192 - (void)minusClick:(NSNotification *)note
193 {
194     // 取出cell(通知的发布者)
195     XMGWineCell *cell = note.object;
196     // 计算总价
197     int totalPrice = self.totalPriceLabel.text.intValue - cell.wine.money.intValue;
198     // 设置总价
199     self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
200     
201     self.buyButton.enabled = totalPrice > 0;
202 }
203 
204 #pragma mark - <UITableViewDataSource>
205 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
206 {
207     return self.wineArray.count;
208 }
209 
210 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
211 {
212     static NSString *ID = @"wine";
213     XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
214     
215     cell.wine = self.wineArray[indexPath.row];
216     
217     return cell;
218 }
219 
220 @end
购物车(通知)

Main.storyboard

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车

 

三、代理


这是我总结的关于协议和代理

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车 

这里有两句话写错了,应该是“要想其他对象成为我的代理,必须先遵守我的协议” 

cell.delegate = self; // 设置控制器为cell的代理

@interface ViewController () <UITableViewDataSource, XMGWineCellDelegate, UITableViewDelegate> // 控制器必须先遵守cell的协议

这就相当于设置TableView(TableView.delegate = self)的代理为控制器时,控制器也要遵守<UITableViewDelegate>这个协议

依次类推,dog对象,cat对象,person对象,任何对象想成为我的代理,都必须遵守我的协议

 

那么在这个例子中

cell内部:

// 通知代理(调用代理的方法)
    // respondsToSelector:能判断某个对象是否实现了某个方法
    if ([self.delegate respondsToSelector:@selector(wineCellDidClickPlusButton:)]) {
        [self.delegate wineCellDidClickPlusButton:self];
    }

// 通知代理(调用代理的方法)
    if ([self.delegate respondsToSelector:@selector(wineCellDidClickMinusButton:)]) {
        [self.delegate wineCellDidClickMinusButton:self];
    }

控制器:

#pragma mark - <XMGWineCellDelegate>
- (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell
{
    // 计算总价
    int totalPrice = self.totalPriceLabel.text.intValue - wineCell.wine.money.intValue;
    
    // 设置总价
    self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
    
    self.buyButton.enabled = totalPrice > 0;
    
    // 将商品从购物车中移除
    if (wineCell.wine.count == 0) {
        [self.wineCar removeObject:wineCell.wine];
    }
}

- (void)wineCellDidClickPlusButton:(XMGWineCell *)wineCell
{
    // 计算总价
    int totalPrice = self.totalPriceLabel.text.intValue + wineCell.wine.money.intValue;
    
    // 设置总价
    self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
    
    self.buyButton.enabled = YES;
    
    // 如果这个商品已经在购物车中,就不用再添加
    if ([self.wineCar containsObject:wineCell.wine]) return;
    
    // 添加需要购买的商品
    [self.wineCar addObject:wineCell.wine];
}

这样通过通知代理,调用代理方法来实现点击加号减号按钮时计算总价

 

部分代码:

iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车iOS开发——UI进阶篇(五)通知、代理、kvo的应用和对比,购物车
  1 /****************** XMGWineCell.h****************/
  2 #import <UIKit/UIKit.h>
  3 @class XMGWine, XMGCircleButton, XMGWineCell;
  4 
  5 @protocol XMGWineCellDelegate <NSObject>
  6 @optional
  7 - (void)wineCellDidClickPlusButton:(XMGWineCell *)wineCell;
  8 - (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell;
  9 @end
 10 
 11 @interface XMGWineCell : UITableViewCell
 12 @property (strong, nonatomic) XMGWine *wine;
 13 
 14 /** 代理对象 */
 15 @property (nonatomic, weak) id<XMGWineCellDelegate> delegate;
 16 @end
 17 
 18 
 19 /****************** XMGWineCell.m****************/
 20 
 21 #import "XMGWineCell.h"
 22 #import "XMGWine.h"
 23 #import "XMGCircleButton.h"
 24 
 25 @interface XMGWineCell()
 26 @property (weak, nonatomic) IBOutlet XMGCircleButton *minusButton;
 27 @property (weak, nonatomic) IBOutlet UIImageView *imageImageView;
 28 @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
 29 @property (weak, nonatomic) IBOutlet UILabel *moneyLabel;
 30 @property (weak, nonatomic) IBOutlet UILabel *countLabel;
 31 @end
 32 
 33 @implementation XMGWineCell
 34 
 35 - (void)setWine:(XMGWine *)wine
 36 {
 37     _wine = wine;
 38     
 39     self.imageImageView.image = [UIImage imageNamed:wine.image];
 40     self.nameLabel.text = wine.name;
 41     self.moneyLabel.text = wine.money;
 42     self.countLabel.text = [NSString stringWithFormat:@"%d", wine.count];
 43     self.minusButton.enabled = (wine.count > 0);
 44 }
 45 
 46 /**
 47  *  加号点击
 48  */
 49 - (IBAction)plusClick {
 50     // 修改模型
 51     self.wine.count++;
 52     
 53     // 修改数量label
 54     self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count];
 55     
 56     // 减号能点击
 57     self.minusButton.enabled = YES;
 58     
 59     // 通知代理(调用代理的方法)
 60     // respondsToSelector:能判断某个对象是否实现了某个方法
 61     if ([self.delegate respondsToSelector:@selector(wineCellDidClickPlusButton:)]) {
 62         [self.delegate wineCellDidClickPlusButton:self];
 63     }
 64 }
 65 
 66 /**
 67  *  减号点击
 68  */
 69 - (IBAction)minusClick {
 70     // 修改模型
 71     self.wine.count--;
 72     
 73     // 修改数量label
 74     self.countLabel.text = [NSString stringWithFormat:@"%d", self.wine.count];
 75     
 76     // 减号按钮不能点击
 77     if (self.wine.count == 0) {
 78         self.minusButton.enabled = NO;
 79     }
 80     
 81     // 通知代理(调用代理的方法)
 82     if ([self.delegate respondsToSelector:@selector(wineCellDidClickMinusButton:)]) {
 83         [self.delegate wineCellDidClickMinusButton:self];
 84     }
 85 }
 86 @end
 87 
 88 /****************** ViewController****************/
 89 #import "ViewController.h"
 90 #import "XMGWineCell.h"
 91 #import "XMGWine.h"
 92 #import "MJExtension.h"
 93 
 94 @interface ViewController () <UITableViewDataSource, XMGWineCellDelegate, UITableViewDelegate>
 95 @property (weak, nonatomic) IBOutlet UIButton *buyButton;
 96 @property (weak, nonatomic) IBOutlet UITableView *tableView;
 97 /** 酒数据 */
 98 @property (nonatomic, strong) NSArray *wineArray;
 99 /** 总价 */
100 @property (weak, nonatomic) IBOutlet UILabel *totalPriceLabel;
101 
102 /** 购物车对象(存放需要购买的商品) */
103 @property (nonatomic, strong) NSMutableArray *wineCar;
104 @end
105 
106 @implementation ViewController
107 - (NSMutableArray *)wineCar
108 {
109     if (!_wineCar) {
110         _wineCar = [NSMutableArray array];
111     }
112     return _wineCar;
113 }
114 
115 - (NSArray *)wineArray
116 {
117     if (!_wineArray) {
118         self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"];
119     }
120     return _wineArray;
121 }
122 
123 - (void)viewDidLoad {
124     [super viewDidLoad];
125     
126     
127 }
128 
129 #pragma mark - <XMGWineCellDelegate>
130 - (void)wineCellDidClickMinusButton:(XMGWineCell *)wineCell
131 {
132     // 计算总价
133     int totalPrice = self.totalPriceLabel.text.intValue - wineCell.wine.money.intValue;
134     
135     // 设置总价
136     self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
137     
138     self.buyButton.enabled = totalPrice > 0;
139     
140     // 将商品从购物车中移除
141     if (wineCell.wine.count == 0) {
142         [self.wineCar removeObject:wineCell.wine];
143     }
144 }
145 
146 - (void)wineCellDidClickPlusButton:(XMGWineCell *)wineCell
147 {
148     // 计算总价
149     int totalPrice = self.totalPriceLabel.text.intValue + wineCell.wine.money.intValue;
150     
151     // 设置总价
152     self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
153     
154     self.buyButton.enabled = YES;
155     
156     // 如果这个商品已经在购物车中,就不用再添加
157     if ([self.wineCar containsObject:wineCell.wine]) return;
158     
159     // 添加需要购买的商品
160     [self.wineCar addObject:wineCell.wine];
161 }
162 
163 #pragma mark - 按钮点击
164 - (IBAction)clear {
165     // 将模型里面的count清零
166     for (XMGWine *wine in self.wineCar) {
167         wine.count = 0;
168     }
169     
170     // 刷新
171     [self.tableView reloadData];
172     
173     // 清空购物车数据
174     [self.wineCar removeAllObjects];
175     
176     self.buyButton.enabled = NO;
177     self.totalPriceLabel.text = @"0";
178 }
179 
180 - (IBAction)buy {
181     // 打印出所有要买的东西
182     for (XMGWine *wine in self.wineCar) {
183         NSLog(@"%d件【%@】", wine.count, wine.name);
184     }
185 }
186 
187 #pragma mark - <UITableViewDataSource>
188 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
189 {
190     return self.wineArray.count;
191 }
192 
193 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
194 {
195     static NSString *ID = @"wine";
196     XMGWineCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
197     
198     cell.delegate = self;
199     cell.wine = self.wineArray[indexPath.row];
200     
201     return cell;
202 }
203 
204 @end
购物车(代理)

 

四、KVO


关于kvo的介绍在前面有一章节专门讲过,那这里就不多说了

懒加载时添加监听

- (NSArray *)wineArray
{
    if (!_wineArray) {
        self.wineArray = [XMGWine objectArrayWithFilename:@"wine.plist"];
        
        // 添加监听 监听count这个属性值的变化
        for (XMGWine *wine in self.wineArray) {
            [wine addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
        }
    }
    return _wineArray;
}

 

只要count属性值有变化,就会调用下面这个方法

#pragma mark - KVO监听
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(XMGWine *)wine change:(NSDictionary *)change context:(void *)context
{
    // NSKeyValueChangeNewKey == @"new"
    int new = [change[NSKeyValueChangeNewKey] intValue];
    // NSKeyValueChangeOldKey == @"old"
    int old = [change[NSKeyValueChangeOldKey] intValue];
    
    if (new > old) { // 增加数量 点击了加号按钮
        int totalPrice = self.totalPriceLabel.text.intValue + wine.money.intValue;
        self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
        self.buyButton.enabled = YES;
    } else { // 数量减小 点击了减号按钮
        int totalPrice = self.totalPriceLabel.text.intValue - wine.money.intValue;
        self.totalPriceLabel.text = [NSString stringWithFormat:@"%d", totalPrice];
        self.buyButton.enabled = totalPrice > 0;
    }
}

 

五、代理、通知、KVO对比


1、 通知(NSNotificationCenter\NSNotification)
  任何对象之间都可以传递消息
  使用范围:
    1个对象可以发通知给N个对象
    1个对象可以接受N个对象发出的通知

  缺点:
    必须得保证通知的名字在发出和监听时是一致的


2、 KVO
  仅仅是能监听对象属性的改变(灵活度不如通知和代理)


3、 代理
  使用范围
    1个对象只能设置一个代理(假设这个对象只有1个代理属性)
    1个对象能成为多个对象的代理
  比`通知`规范(包括命名规范、利用程序员之间的交流)
  建议使用`代理`多于`通知`