iOS中KVO,KVC的學習記錄 – iPhone手機開發技術文章 iPhone軟體開發教學課程

iOS中KVO,KVC的學習記錄

KVO

#import 
#import "BankAccount.h"

@interface Person : NSObject
{
    BankAccount *bankAccount;
}

- (void)registerAsObserver;

@end

#import "Person.h"

@implementation Person

- (void)dealloc{
    bankAccount = nil;
}

- (id)init{
    self = [super init];
    if (self) {
        bankAccount = [[BankAccount alloc]init];
    }
    return self;
}

//OpeningBalance 指向自己的指針
static void *OpeningBalance = (void *)&OpeningBalance;
- (void)registerAsObserver{
    //監聽銀行賬號的變化過程
    
    [bankAccount addObserver:self forKeyPath:@"openingBalance" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:OpeningBalance];
    //給銀行賬號bankAccount 增加一個監聽者 self,監聽openingBalance的變化過程
    //隻要openingBalance有變化,就會讓self知道
    //隻要有變化,隻要有新的值
}

- (void)unregisterObserver{
    //self從bankAccount 中解除監聽對象
    [bankAccount removeObserver:self forKeyPath:@"openingBalance"];
}

//監聽回調的函數
//bankAccount 裡面openingBalance 有變化瞭,就會調用下面的方法
//keyPath 表示之前監聽的key 也就是openingBalance
//object 表示bankAccount
//change 字典 包含瞭新,舊的值
//context是私有變量OpeningBalance
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    
//    [keyPath isEqualToString:@"openingBalance"]
    if (context == OpeningBalance) {
        NSString *v = [change objectForKey:NSKeyValueChangeNewKey];
        NSLog(@"v is %@",v);
    }else{
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

@end

#import 

@interface BankAccount : NSObject
{
    float _openingBalance;
}

//賬號餘額
@property(nonatomic,assign)float openingBalance;

@end

#import "BankAccount.h"

@implementation BankAccount

@synthesize openingBalance = _openingBalance;

- (id)init{
    self = [super init];
    if (self) {
        [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(balanceUpdate:) userInfo:nil repeats:YES];
    }
    return self;
}

- (void)balanceUpdate:(id)arg
{
    float f = self.openingBalance;
    
    f += arc4random()%100;
    
//    _openingBalance = f;不能這麼寫
    
    //1.
    //self.openingBalance = f;
    
    //2.
    //[self setOpeningBalance:f];
    
    //3.
    //[self setValue:[NSNumber numberWithFloat:f] forKey:@"openingBalance"];
    
    //4.
    [self willChangeValueForKey:@"openingBalance"];
    _openingBalance = f;
    [self didChangeValueForKey:@"openingBalance"];
    
    
}

@end

KVC

PlayList


#import 
#import "PlayItem.h"
@interface PlayList : NSObject
{
    int _number;
    NSString *_name;
    //當前播放列表
    PlayItem *_currItem;
    NSMutableArray *_itemList;
}

@property(nonatomic, strong)NSMutableArray *itemList;
@property(nonatomic, assign)int number;
@property(nonatomic, strong)NSString *name;
@property(nonatomic, strong)PlayItem *currItem;

@end


#import "PlayList.h"

@implementation PlayList

@synthesize number = _number, name = _name,currItem = _currItem,itemList = _itemList;


- (id)init{
    self = [super init];
    if (self) {
        
        self.currItem = [[PlayItem alloc]init];
        
        self.itemList = [NSMutableArray array];
        
        for (int i = 0; i < 20; i++) {
            PlayItem *pi = [[PlayItem alloc]init];
            pi.name = [NSString stringWithFormat:@"name %d",i];
            pi.price = 100+i;
            [self.itemList addObject:pi];
        }
        
    }
    
    return self;
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key  {
    NSLog(@"file is %s function %@ is calling",__FILE__,NSStringFromSelector(_cmd));
}

- (void)dealloc{
    self.name = nil;
    self.currItem = nil;
    self.itemList = nil;
}

@end

PlayItem

@interface PlayItem : NSObject
{
    NSString *_name;
    float _price;
}

@property(nonatomic, strong)NSString *name;
@property(nonatomic, assign)float price;

@end


#import "PlayItem.h"

@implementation PlayItem

@synthesize price = _price;
@synthesize name = _name;

- (void)dealloc{
    self.name = nil;
}

//如果設置裡面不存在的key就會觸發該方法
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"function %@ is calling",NSStringFromSelector(_cmd) );
}
@end

main.m


#import 
#import "PlayList.h"
#import "PlayItem.h"
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        PlayList *pl = [[PlayList alloc]init];
        [pl setValue:@"播放列表" forKey:@"name"];
        NSLog(@"name is %@",pl.name);
        
        id v = [pl valueForKey:@"number"];
        NSLog(@" v is %@",v);
        
        //設置pl currItem.name 字段
        [pl setValue:@"播放列表22" forKeyPath:@"currItem.name"];
        
        NSLog(@"pl.currItem.name is %@",pl.currItem.name);
        
        //設置一批key value
        NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"200",@"number",@"測試",@"name", nil];
        [pl setValuesForKeysWithDictionary:dict];
        NSLog(@"name is %@ number is %d",pl.name,pl.number);
        
        //pl對象裡沒有test這個key,所以系統崩潰
        [pl setValue:@"hello" forKey:@"test"];
        
        id obj = [pl valueForKey:@"itemList"];
        NSLog(@"obj is %@",obj);
        
        id obj2 = [pl valueForKeyPath:@"itemList.name"];
        NSLog(@"obj name is %@",obj2);
        
//        NSLog(@"%@",pl.itemList.) 用點語法無法取到更深的值
        
        //求和,平均值,最大值,最小值
        NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@sum.price"]);
        NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@avg.price"]);
        NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@max.price"]);
        NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@min.price"]);
        
    }
    return 0;
}

自己的理解


//對比
        //如果要想不用kvc的話要取值則要麻煩的多
        int sum = 0;
        for (PlayItem *item in pl.itemList){
            sum += item.price;
        }
        NSLog(@"num == %d",sum);
        
        //而kvc隻需要
        NSLog(@"itemlist price sum is %@",[pl.itemList valueForKeyPath:@"@sum.price"]);
        
        //如果要想取到PlayList 裡的 PlayItem 裡的name 不用kvc是報錯的,因為點語法無法取到更深的值
        // NSLog(@"%@",pl.itemList.) ;
        
        
        //kvc隻需要
        id obj3 = [pl valueForKeyPath:@"itemList.name"];
        NSLog(@"%@",obj3);
        
        //相比較kvc使代碼更加簡潔有效 已讀

參考文章:設計模式-KVO

如果想瞭解的更詳細,可以參考這兩篇文章:[深入淺出Cocoa]詳解鍵值觀察(KVO)及其實現機理 (譯)KVO的內部實現

發佈留言