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和实体对象属性的单词必须是一样的(大小可以忽略),这里没有使用外部映射文件主要也是为了简化代码和项目的需要决定的。