1. 程式人生 > >Objective-C中ORM的運用:實體物件和字典的相互自動轉換

Objective-C中ORM的運用:實體物件和字典的相互自動轉換

iOS開發中基於ORM的框架很多,如SQLitePersistentObject,實際開發中需求不同或場景不同,方式方法也就不同,有時專案中用不上ORM框架,或者出於公司或專案組習慣或規範、實際專案需求或技術要求等等原因,不會採用完整的ORM框架,但一些重複囉嗦的程式碼使用一定的ORM功能還是很能提高效率的。

基於效能或靈活性考慮,或複雜查詢的需求,或專案組要求,專案中資料庫存取一般直接用SQL或用FMDB的多些(某些產品研發型另說,軟體架構設計是另一個話題,從筆者N年面試N多iOS開發者來看用FMDB的佔了極大多數,不乏某某有名App),程式碼中使用字典、陣列或自定義類(或叫實體)作為資料載體,FMDB的FMResultSet有個resultDictionary能夠直接返回字典NSDictionary,再結合下面的輔助類,能夠解決實體物件和字典(NSDictionary)的相互自動轉換問題,不用一個Key一個Key,一個屬性一個屬性的自己去寫程式碼了,避免重複手寫煩雜和拼寫錯誤的可能,大大的提高了開發效率。

//
//  EntityHelper.h
//  使用前提條件是:字典的Key和實體物件屬性的單詞是一樣的,大小可以忽略。
//
//  Created by LongJun on 13-1-28.
//  Copyright (c) 2013年 RL. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface EntityHelper : NSObject


//字典物件轉為實體物件
+ (void) dictionaryToEntity:(NSDictionary *)dict entity:(NSObject*)entity;

//實體物件轉為字典物件
+ (NSDictionary *) entityToDictionary:(id)entity;

@end
//
//  EntityHelper.m
//  ARProjectForPad
//
//  Created by LongJun on 13-1-28.
//  Copyright (c) 2013年 RL. All rights reserved.
//

#import "EntityHelper.h"
#import <objc/runtime.h>

@implementation EntityHelper

#pragma mark - Custom Method

+ (void) dictionaryToEntity:(NSDictionary *)dict entity:(NSObject*)entity
{
    if (dict && entity) {
        
        for (NSString *keyName in [dict allKeys]) {
            //構建出屬性的set方法
            NSString *destMethodName = [NSString stringWithFormat:@"set%@:",[keyName capitalizedString]]; //capitalizedString返回每個單詞首字母大寫的字串(每個單詞的其餘字母轉換為小寫)
            SEL destMethodSelector = NSSelectorFromString(destMethodName);
            
            if ([entity respondsToSelector:destMethodSelector]) {
                [entity performSelector:destMethodSelector withObject:[dict objectForKey:keyName]];
            }
            
        }//end for
        
    }//end if
}

+ (NSDictionary *) entityToDictionary:(id)entity
{
    
    Class clazz = [entity class];
    u_int count;
    
    objc_property_t* properties = class_copyPropertyList(clazz, &count);
    NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
    NSMutableArray* valueArray = [NSMutableArray arrayWithCapacity:count];
    
    for (int i = 0; i < count ; i++)
    {
        objc_property_t prop=properties[i];
        const char* propertyName = property_getName(prop);
        
        [propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
        
        //        const char* attributeName = property_getAttributes(prop);
        //        NSLog(@"%@",[NSString stringWithUTF8String:propertyName]);
        //        NSLog(@"%@",[NSString stringWithUTF8String:attributeName]);
        
        id value =  [entity performSelector:NSSelectorFromString([NSString stringWithUTF8String:propertyName])];
        if(value ==nil)
            [valueArray addObject:[NSNull null]];
        else {
            [valueArray addObject:value];
        }
        //        NSLog(@"%@",value);
    }
    
    free(properties);
    
    NSDictionary* returnDic = [NSDictionary dictionaryWithObjects:valueArray forKeys:propertyArray];
    NSLog(@"%@", returnDic);
    
    return returnDic;
}


@end


實際使用(邏輯層)示例:

//業務需要返回實體物件
- (UserSCOInfoEntity*)loadStudyRecord:(UserSCOInfoQuery*)query
{
    
    UserSCOInfoEntity *userSCOInfo = nil;
    @try {
        
        //
        NSDictionary *resultDict = [self loadStudyRecordForDict:query];
        if (!resultDict) return nil;
        //字典值自動填充到實體物件屬性
        [EntityHelper dictionaryToEntity:resultDict entity:userSCOInfo];
        
    }
    @catch (NSException *exception) {
        NSAssert1(0, @"Exception=%@", exception.reason);
    }
    @finally {
    }
    return userSCOInfo;
}

//業務需要直接返回字典
- (NSDictionary*)loadStudyRecordForDict:(UserSCOInfoQuery*)query
{
    if (!query || !query.userID || !query.courseID || !query.scoID || !query.type || !query.typeID) {
        NSAssert(0, @"UserSCOInfoQuery物件或屬性不能為空");
        return nil;
    }
    
    NSDictionary *resultDict = nil;
    FMDatabase *db = [FMDatabase databaseWithPath:[Common sharedInstance].localMainDb];
    @try {
        if (![db open]) {
            [db release];
            //NSLog(@"db open fail");
            return nil;
        }
        
        FMResultSet *s = [db executeQuery:@"SELECT … "];
        while ([s next]) {
            resultDict = [s resultDictionary];
            break;
        }
        [s close];
        
        if (!resultDict) {
//            NSString *errMsg = [db lastErrorMessage];
            //NSLog(@"[db lastErrorMessage]=%@",errMsg);
        }
    }
    @catch (NSException *exception) {
        NSAssert1(0, @"Exception=%@", exception.reason);
    }
    @finally {
        [db close];
    }
    return resultDict;
}


當然,以上程式碼有一定應用場景,有一定的侷限性,比如:
字典的Key和實體物件屬性的單詞必須是一樣的(大小可以忽略),這裡沒有使用外部對映檔案主要也是為了簡化程式碼和專案的需要決定的。