【原】IOS中KVO模式的解析與應用


最近老翁在項目中多處用到了KVO,深感這種模式的好處。現總結如下:

一、概述

KVO,即:Key-Value Observing,它提供一種機制,當指定的對象的屬性被修改后,則對象就會接受到通知。簡單的說就是每次指定的被觀察的對象的屬性被修改后,KVO就會自動通知相應的觀察者了。

KVO其實也是“觀察者”設計模式的一種應用。我的看法是,這種模式有利於兩個類間的解耦合,尤其是對於 業務邏輯與視圖控制 這兩個功能的解耦合。

 

二、引子

先來看個引子:

有一個業務類:Walker,在這個類內部只負責關於業務邏輯的處理,比如負責從服務器傳來的JSON中解析數據,或做其他業務數據上的處理。

有另一個類:ViewController,專門負責界面的交互與試圖更新。其中,需要講Walker的某些屬性顯示出來,並實時更新。

目前,據我所能想到的方法有以下幾種:

方法1、直接的函數調用

Walker的類內部,把創建一個ViewController的對象,然后調用ViewController的修改界面的方法,把需要改動的屬性值作為形參傳給該函數。

這種方式最直觀,因為它不需要繞任何彎子。但是,確實最糟的方法。因為Walker與ViewController這兩個類從此緊緊耦合在一起了。記住這句話,處理業務邏輯的類,對外部的事情知道得越少越好。甚至於,要做到外部是否有VC(View Controller),有多少個VC都不影響我。假設這是一個項目,程序員A負責業務邏輯的處理,程序員B負責UI,則采取這種方式后,程序員A就受制於B,互相干擾。

 

方法2、利用消息通信機制(NSNotification)

在Walker內部建立消息中心NSNotificationCenter,把實例化之后的VC對象作為observer。Center建在Walker或者VC都無所謂,具體看我博客的另一篇文章【NSNotificationCenter總結】。這種方法比前面的方法好一些。但是有一個很大的缺點:如果Walker需要更改的屬性很多而且很頻繁,那么這種方式很不方便傳值。而且,注意到了沒,“把實例化后的VC對象作為observer”,始終逃不開在Walker內部對VC實例化。依舊是耦合着。

 

方法3、利用delegate

關於delegate的介紹有很多,這里就不多講。但是在這種需求下用 delegate,有點“殺雞用牛刀”感覺,成本較大,而且不直觀。

 

方法4、利用KVO模式

所有的代碼都將在ViewController中實現。對於Walker,它自己都不知道外部是否有VC,以及VC會怎用用我的屬性。

 

三、Demo

 

 1 //
 2 //  Walker.h
 3 //  KVOExample
 4 //
 5 //  Created by 老翁 on 13-7-29.
 6 //  Copyright (c) 2013年 wzl. All rights reserved.
 7 //
 8 
 9 #import <Foundation/Foundation.h>
10 
11 @interface Walker : NSObject
12 {
13     NSInteger _age;
14     NSString *_name;
15 }
16 
17 @property (nonatomic) NSInteger age;
18 @property (nonatomic, retain) NSString *name;
19 
20 - (id)initWithName:(NSString *)name age:(NSInteger)age;
21 
22 
23 @end
#import "Walker.h"

@implementation Walker

@synthesize age = _age;
@synthesize name = _name;

- (void)dealloc
{
    [_name release];
    [super dealloc];
}

- (id)initWithName:(NSString *)name age:(NSInteger)age
{
    if (self = [super init]) {
        _name = name;
        _age = age;
    }
    return  self;
}


@end

 

ViewController代碼:

//
//  ViewController.h
//  KVOExample
//
//  Created by 老翁 on 13-7-29.
//  Copyright (c) 2013年 wzl. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "Walker.h"

@class Walker;

@interface ViewController : UIViewController
{
    Walker *_walker;
    UILabel *_ageLabel;
}

@property (nonatomic, assign) Walker *walker;

@end

 

 1 //
 2 //  ViewController.m
 3 //  KVOExample
 4 //
 5 //  Created by 老翁 on 13-7-29.
 6 //  Copyright (c) 2013年 wzl. All rights reserved.
 7 //
 8 
 9 #import "ViewController.h"
10 
11 @interface ViewController ()
12 
13 @end
14 
15 @implementation ViewController
16 
17 @synthesize walker = _walker;
18 
19 
20 - (void)dealloc
21 {
22     [_walker release];
23     [_ageLabel release];
24     [super dealloc];
25 }
26 
27 - (void)viewDidLoad
28 {
29     [super viewDidLoad];
30     // Do any additional setup after loading the view, typically from a nib.
31     _walker = [[Walker alloc] initWithName:@"老翁" age:25];
32     /* 關鍵步驟 */
33     [_walker addObserver:self
34               forKeyPath:@"age"
35                  options:NSKeyValueObservingOptionNew
36                  context:nil];
37     
38     
39     //UI initialization
40     UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
41     [btn setFrame:CGRectMake(120, 200, 100, 35)];
42     [btn setBackgroundColor:[UIColor lightGrayColor]];
43     [btn setTitle:@"增加5歲" forState:UIControlStateNormal];
44     [btn addTarget:self
45             action:@selector(buttonPressed)
46   forControlEvents:UIControlEventTouchUpInside];
47     [self.view addSubview:btn];
48     
49     _ageLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 150, 200, 35)];
50     _ageLabel.text = [NSString stringWithFormat:@"%@現在的年齡是: %d", _walker.name, _walker.age];
51     _ageLabel.backgroundColor = [UIColor clearColor];
52     [self.view addSubview:_ageLabel];
53     
54     
55 }
56 
57 - (void)buttonPressed
58 {
59     _walker.age += 5;
60 }
61 
62 /* KVO function, 只要object的keyPath屬性發生變化,就會調用此函數*/
63 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
64 {
65     if ([keyPath isEqualToString:@"age"] && object == _walker) {
66         _ageLabel.text = [NSString stringWithFormat:@"%@現在的年齡是: %d", _walker.name, _walker.age];
67     }
68 }
69 
70 
71 
72 - (void)didReceiveMemoryWarning
73 {
74     [super didReceiveMemoryWarning];
75     // Dispose of any resources that can be recreated.
76 }
77 
78 @end

 

  

點擊了按鈕之后,系統會調用 observeValueForKeyPath :函數,這個函數應該也是回調函數。在該函數內部做UI更新。我們以這種輕量級的方式達到了目的。

 

##################################################

聲明 轉載請注明原文地址:

http://www.cnblogs.com/wengzilin/p/3223770.html

歡迎私下交流學習:

QQ 719113951

##################################################


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM