ios開發runtime學習五:KVC以及KVO,利用runtime實現字典轉模型

來源:互聯網
上載者:User

標籤:

一:KVC和KVO的學習

#import "StatusItem.h"/* 1:總結:KVC賦值:1:setValuesForKeysWithDictionary實現原理:遍曆字典,得到所有的key,value值,再利用kvc, setVaue forkey來為value賦值 2: [item setValue:@"來自即刻筆記" forKey:@"source"],內部的底層實現, 1.首先去模型中尋找有沒有setSource,找到,直接調用賦值 [self setSource:@"來自即刻筆記"] 2.去模型中尋找有沒有source屬性,有,直接存取屬性賦值  source = value 3.去模型中尋找有沒有_source屬性,有,直接存取屬性賦值 _source = value 4.找不到,就會直接報錯 setValue:forUndefinedKey:報找不到的錯誤  當系統找不到就會調用這個方法,報錯- (void)setValue:(id)value forUndefinedKey:(NSString *)key可以重寫此方法更改key值 3:KVC四個方法:利用kvc可以訪問成員變數和屬性,setValue,value為屬性值,forKey,key為屬性名稱,forKeyPath為索引值路徑,例如在model中有如下屬性定義:    @property (nonatomic, strong) BankAccount *account;    keyPath:    [zhangSan setValue:@150 forKeyPath:@"account.balance"];  - (id)valueForKey:(NSString *)key; - (id)valueForKeyPath:(NSString *)keyPath; - (void)setValue:(id)value forKey:(NSString *)key; - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;  4:KVO:索引值觀察機制,用於對屬性的value值的改變做監聽:用法: @interface BankAccount : NSObject @property (nonatomic, assign) NSInteger balance; @end  @interface Person : NSObject @property (nonatomic, strong) BankAccount *account; @end  @implementation Person - (instancetype)init { ... // 註冊Observer: [self.account addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; ... }  - (void)dealloc { // 不要忘了removeObserver [self.account removeObserver:self forKeyPath:@"balance"]; }  // 屬性變化的回調方法: - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"balance"]) { NSLog(@"Balance was %@.", change[NSKeyValueChangeOldKey]); NSLog(@"Balance is %@ now.", change[NSKeyValueChangeNewKey]); } } @end  - (void)testKVO { Person *zhangSan = [[Person alloc] initWithName:@"ZhangSan" andBalance:20]; // 無論是用點文法還是KVC的方法都會觸發回調: zhangSan.account.balance = 150; [zhangSan setValue:@250 forKeyPath:@"account.balance"]; }  */@interface StatusItem ()@property (nonatomic,copy)NSString *hello;@end@implementation StatusItem// 模型只儲存最重要的資料,導致模型的屬性和字典不能一一對應+ (instancetype)itemWithDict:(NSDictionary *)dict{    StatusItem *item = [[self alloc] init];        // KVC:把字典中所有值給模型的屬性賦值    [item setValuesForKeysWithDictionary:dict];        // 拿到每一個模型屬性,去字典中取出對應的值,給模型賦值    // 從字典中取值,不一定要全部取出來    // MJExtension:字典轉模型 runtime:可以把一個模型中所有屬性遍曆出來    // MJExtension:封裝了很多層//    item.pic_urls = dict[@"pic_urls"];//    item.created_at = dict[@"created_at"];        // KVC原理:    // 1.遍曆字典中所有key,去模型中尋找有沒有對應的屬性    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {                // 2.去模型中尋找有沒有對應屬性 KVC        // key:source value:來自即刻筆記        // [item setValue:@"來自即刻筆記" forKey:@"source"]        [item setValue:value forKey:key];                    }];        return item;}// 重寫系統方法? 1.想給系統方法添加額外功能 2.不想要系統方法實現// 系統找不到就會調用這個方法,報錯- (void)setValue:(id)value forUndefinedKey:(NSString *)key{    }@end

二:利用runtime實現字典轉模型

#import "ViewController.h"#import "NSDictionary+Property.h"#import "StatusItem.h"#import "NSObject+Model.h"@interface ViewController ()@end/* 總結:1:項目中的檔案都儲存在mainBundle裡,讀取項目中的本地資訊:[[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; 得到本地路徑path,再看項目中的檔案根節點是字典還是數組,再從中讀取本地路徑filer:dictionaryWithContentsOfFile讀取 。若是擷取網路端的路徑:dictionaryWithContentsOfUrl */@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];           // 擷取檔案全路徑    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];        // 檔案全路徑    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];        // 設計模型,建立屬性代碼 => dict//    [dict[@"user"] createPropertyCode];        // 字典轉模型:KVC,MJExtension    StatusItem *item = [StatusItem modelWithDict:dict];        }@end
#import <Foundation/Foundation.h>// 字典轉模型@interface NSObject (Model)+ (instancetype)modelWithDict:(NSDictionary *)dict;@end
#import "NSObject+Model.h"#import <objc/message.h>/* 總結:MJExtension字典轉模型的底層核心實現:runtime實現字典轉模型。  1:因為模型model都繼承NSObject,所以可以給系統類別寫分類進行拓展,子類繼承NSObject,也就是繼承了擴充的方法。所以模型轉字典的方法考慮給NSObject寫一個分類進行方法的拓展 2:在分類中若是對象方法,self指的是調用該方法的對象,類方法中self指的是調用該方法的類。方法的設計:類方法簡單粗暴,直接用類去調用,字典轉模型方法獲得所轉換的模型,不用建立對象,並且會將調用方法的類作為參數傳進方法中(對象方法也如此)  3:原理:runtime:根據模型中屬性,去字典中取出對應的value給模型屬性賦值 1:先擷取模型中所有成員變數 key 參數意義: // 擷取哪個類的成員變數 // count:成員變數個數 int *類型 unsigned int count = 0; // 擷取成員變數數組 Ivar *ivarList = class_copyIvarList(self, &count);  注意:1:int *count ,此count的類型為int *類型,當作為參數的時候,需要傳入一個int *類型的指標,指標裡存放的都是記憶體位址,也就是將地址作為參數傳遞,當方法執行完畢後,系統會拿到*count 進行賦值 int a = 2; int b = 3; int c = 4; int arr[] = {a,b,c}; int *p = arr; p[0]; NSLog(@"%d %d",p[0],p[1]);      2: 擷取類裡面屬性        class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)         擷取類裡面所有方法        class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)// 本質:建立誰的對象      3:擷取模型中所有成員變數 key,Ivar:成員變數 以底線開頭,相當於一個數組 // 擷取哪個類的成員變數 // count:成員變數個數 unsigned int count = 0; // 擷取成員變數數組 Ivar *ivarList = class_copyIvarList(self, &count);  4:具體實現:擷取成員變數名字:ivar_getName(ivar),屬於c語言字串,所以要進行UTF8編碼  擷取成員變數類型:ivar_getTypeEncoding(ivar) // 擷取成員變數名字 NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 擷取成員變數類型 NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];  注意:1:字串替換:stringByReplacingOccurrencesOfString  2:字串截取:substringFromIndex:包括該index   substringToIndex:不包括該index 3:前尾碼:hasPrefix首碼,hasSuffix:尾碼 4:是否包含某個字串:containString 5:字串轉換為Class:NSClassFromString: // 擷取類 Class modelClass = NSClassFromString(ivarType);  value = [modelClass modelWithDict:value];  6:一般某個方法接受傳遞進來的參數的時候,要判斷參數是否為空白,為nil或是為空白值,給某個值賦值的時候,也要判斷該值是否存在: // 給模型中屬性賦值 if (value) { [objc setValue:value forKey:key]; }  */@implementation NSObject (Model)// Ivar:成員變數 以底線開頭// Property:屬性+ (instancetype)modelWithDict:(NSDictionary *)dict{    id objc = [[self alloc] init];        // runtime:根據模型中屬性,去字典中取出對應的value給模型屬性賦值    // 1.擷取模型中所有成員變數 key    // 擷取哪個類的成員變數    // count:成員變數個數    unsigned int count = 0;    // 擷取成員變數數組    Ivar *ivarList = class_copyIvarList(self, &count);        // 遍曆所有成員變數    for (int i = 0; i < count; i++) {        // 擷取成員變數        Ivar ivar = ivarList[i];                // 擷取成員變數名字        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];        // 擷取成員變數類型        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];        // @\"User\" -> User        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];        // 擷取key        NSString *key = [ivarName substringFromIndex:1];                // 去字典中尋找對應value        // key:user  value:NSDictionary                id value = dict[key];                // 二級轉換:判斷下value是否是字典,如果是,字典轉換層對應的模型        // 並且是自訂對象才需要轉換        if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {            // 字典轉換成模型 userDict => User模型            // 轉換成哪個模型            // 擷取類            Class modelClass = NSClassFromString(ivarType);                        value = [modelClass modelWithDict:value];        }                // 給模型中屬性賦值        if (value) {            [objc setValue:value forKey:key];        }    }            return objc;}void test(int *count){    *count = 3;}@end

 

ios開發runtime學習五:KVC以及KVO,利用runtime實現字典轉模型

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.