iOS实现反射机制

利用Objective-C的反射机制和运行时特性实现类静态方法的动态访问(一) 利用Objective-C的反射机制和运行时特性实现类静态方法的动态访问,解决不能使用NSObject类的反射方法 performSelector 访问静态方法的坑爹问题! 阅读详情
移动互联网下iOS客户端的开发,一般都会与服务端进行通讯,也会使用到Sqlite数据库来保存一些数据,按常规的搞法,一般都需要手动建表结构,写实体类对象,然后写插入、更新、查询等语句来实现功能,因此想到是否有一种通用的办法来进行一些代码方面的减负工作。通过这个项目的实践,已经找到了答案。

在使用这些代码前,我们进行了以下的约定:
1. sqlite的数据库表名直接使用实体类的类名;
2. sqlite的数据字段使用实体类的属性名称;
3. sqlite的数据类型统一设为text(引起sqlite这种文本数据库是动态类型的,存储的本质都是文本)
4. 实体类的类型统一都设为NSString

做以上的约定,只是减少使用过程中,由于类型的不同造成不必要的麻烦,如果要支持各种类型,需要编写各种判断代码,进行格式的处理,有兴趣的同学可以进一步研究,呵呵。

下面介绍几个重要的方法。

因为用到了Obj-c运行时的相关方法,所以需要导入运行时的头文件。
#import <objc/runtime.h>

直接扩展了NSObject,名称叫NSObject+Property,文章末尾提供文件下载。

@interface NSObject (Property)
@end

1. 利用反射取得NSObject的属性,并存入到数组中 

- (NSArray *)getPropertyList: (Class)clazz{
u_int count;
objc_property_t *properties = class_copyPropertyList(clazz, &count);
NSMutableArray *propertyArray = [NSMutableArray arrayWithCapacity:count];

for (int i = 0; i < count ; i++)
{
const char* propertyName = property_getName(properties[i]);
[propertyArray addObject: [NSString stringWithUTF8String: propertyName]];
}
free(properties); 
return propertyArray;
}

 

2. 根据属性生成创建Sqlite表的语句 

- (NSString *)tableSql:(NSString *)tablename{
NSMutableString *sql = [[NSMutableString alloc] init];
NSArray *array = [self getPropertyList];
[sql appendFormat:@"create table %@ (",tablename] ;
NSInteger i = 0;
for (NSString *key in array) {
if (i>0) {
[sql appendString:@","];
}
[sql appendFormat:@"%@ text",key];
i++;
}
[sql appendString:@")"];
return sql;
}

 

3. 把一个实体对象,封装成字典Dictionary  

- (NSDictionary *)convertDictionary{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSArray *propertyList = [self getPropertyList];
for (NSString *key in propertyList) {
SEL selector = NSSelectorFromString(key);

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id value = [self performSelector:selector];
#pragma clang diagnostic pop

if (value == nil) {
value = [NSNull null];
}
[dict setObject:value forKey:key];
}
return dict;
}

 


4. 从一个字典中还原成一个实体对象

- (void)dictionaryForObject:(NSDictionary*) dict{
for (NSString *key in [dict allKeys]) {
id value = [dict objectForKey:key];

if (value==[NSNull null]) {
continue;
}
if ([value isKindOfClass:[NSDictionary class]]) {
id subObj = [self valueForKey:key];
if (subObj)
[subObj dictionaryForObject:value];
}
else{
[self setValue:value forKeyPath:key];
}
}
}

 

5. 返回一个对象的类型名称

- (NSString *)className{
return [NSString stringWithUTF8String:object_getClassName(self)];
}

 

以上是对NSObject的一个扩展,使用了Obj-C的Category特性

 

以下是与数据存储相关,定义为DbHelper,使用FMDB库对sqlite进行操作

 

1. 把id类型的数据对象插入到数据库
 

-(void)insertObject:(id)object{
NSString *tablename = [object className];
NSMutableString *sql = [[NSMutableString alloc] init];
NSArray *array = [object getPropertyList];
[sql appendFormat:@"insert into %@ (",tablename] ;
NSInteger i = 0;
for (NSString *key in array) {
if (i>0) {
[sql appendString:@","];
}
[sql appendFormat:@"%@",key];
i++;
}
[sql appendString:@") values ("];
NSMutableArray *arrayValue = [NSMutableArray array];
i=0;
for (NSString *key in array) {
SEL selector = NSSelectorFromString(key);

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id value = [object performSelector:selector];
#pragma clang diagnostic pop


if (value==nil) {
value = @"";
}
[arrayValue addObject:value];
if (i>0) {
[sql appendString:@","];
}
[sql appendString:@"?"];
i++;
}
[sql appendString:@")"];
[_db executeUpdate:sql withArgumentsInArray:arrayValue];
}

 

2. 把字典NSDictionary对象插入到数据库中
在与服务器进行交互时候,我们一般采用Json进行数据通讯,从服务端获取Json字符,通过JSONKit框架,反序列化成NSDictionary对象,然后插入到数据库

生成插入的sql语句

-(NSString *)createInsertSqlByDictionary:(NSDictionary *)dict tablename:(NSString *)table{

NSMutableString *sql = [[NSMutableString alloc] init];
[sql appendFormat:@"insert into %@ (",table] ;
NSInteger i = 0;
for (NSString *key in dict.allKeys) {
if (i>0) {
[sql appendString:@","];
}
[sql appendFormat:@"%@",key];
i++;
}
[sql appendString:@") values ("];
i = 0;
for (NSString *key in dict.allKeys) {
if (i>0) {
[sql appendString:@","];
}
[sql appendFormat:@":%@",key];
i++;
}
[sql appendString:@")"];
return sql;
}

 

把字典插入到数据库中

-(void)insertBySql:(NSString *)sql dict:(NSDictionary *)dict{
if (sql && sql.length>0) {
[_dbQueue inDatabase:^(FMDatabase *db) {
[db executeUpdate:sql withParameterDictionary:dict];}];}}

 

取数据

3. 从数据库取数据,封装成成字典,然后放入到数组中

-(NSArray *)queryDbToDictionaryArray:(NSString *)tablename sql:(NSString *)sql{
FMResultSet *resultSet=[_db executeQuery:sql];
NSArray *columnArray = [self fMSetColumnArray:resultSet];
NSMutableArray *syncArray = [[NSMutableArray alloc] init];
NSString *columnName = nil;
while ([resultSet next])
{
NSMutableDictionary *syncData = [[NSMutableDictionary alloc] init];
for(int i =0;i<columnArray.count;i++)
{
columnName = [columnArray objectAtIndex:i];
NSString *columnValue = [resultSet stringForColumn: columnName];
if (columnValue==nil) {
columnValue=@"";
}
[syncData setObject:columnValue forKey:columnName];
}
[syncArray addObject:syncData];
}
if ([syncArray count]==0) {
return nil;
}

return syncArray;
}

 


4. 从数据库中取数据,封装成对象,然后放入数组中

-(NSArray *)queryDbToObjectArray:(Class )clazz sql:(NSString *)sql{
FMResultSet *resultSet=[_db executeQuery:sql];
NSArray *columnArray = [self fMSetColumnArray:resultSet];
NSMutableArray *syncArray = [[NSMutableArray alloc] init];
NSString *columnName = nil;
while ([resultSet next])
{
NSObject *obj = [[clazz alloc] init];

if (obj==nil) {
continue;
}

for(int i =0;i<columnArray.count;i++)
{
columnName = [columnArray objectAtIndex:i];
NSString *columnValue = [resultSet stringForColumn: columnName];
SEL selector = NSSelectorFromString(columnName);

if ([obj respondsToSelector:selector]) {
[obj setValue:columnValue forKeyPath:columnName ];
}
}
[syncArray addObject:obj];
}
if ([syncArray count]==0) {
return nil;
}
return syncArray;
}

 

在数据量很大时候,考虑到性能问题,此方法需要酌情使用。


好了,最后提供这四个文件DbHelper.h、DbHelper.m、NSObject+Property.h、NSObject+Property.m的下载。
有了这几个东西,进行开发就省去了很多时间和代码量,直接动态生成表,从服务端接口取到数据,直接插入到数据库中保存,显示数据时,从数据库中取出数据放入到对象数组中。

由于项目的保密性,不可能提供整个文件的下载,只提供了关键的几个文件,同学们可以根据需要进行扩展和研究,也欢迎一起探讨,甚至拍砖。

不知道怎么传附件,丢到网站里面了。

http://www.shouji138.com/files/NSObject_DbHelper.zip

 

 补充下如何使用。

1. 插入数据

假设定义如下的实体类

@interface AppInfo : NSObject
@property (nonatomic,strong) NSString *AppId;
@property (nonatomic,strong) NSString *AppName;
@property (nonatomic,strong) NSString *AppIcon;
@property (nonatomic,strong) NSString *AppIntro;
@end

@implementation AppInfo


@end

 

假设从服务端接口中获取到了JSON数据对象
第一步,先将json字符串反序列化成字典NSDictionary

NSDictionary * result = [[operation responseString] objectFromJSONString];

 

第二部,动态创建表,然后把数据保存进去

DbHelper *dbhelper = [DbHelper instance];
NSString *tablename = @"AppInfo";
[dbhelper DropExistsTable:tablename];
[dbhelper createTableByClass:[AppInfo class]];
NSString *insertSql = nil;

//循环插入数据,parmer是json数据里面的一个数组
for (NSDictionary * row in parmer) { 
insertSql = [dbhelper createInsertSqlByDictionary:row tablename:tablename]; 
[dbhelper insertBySql:insertSql dict:row];
}

 

2. 查询数据,放入到数组中

-(NSArray *)getAllApps{
NSString *sql = @"select * from AppInfo " ;
NSArray *result = [_dbhelper queryDbToObjectArray:[AppInfo class] sql:sql];
return result;
}
ios反射机制 有时候我们需要通过类名来生成相应类的实例,这就用到反射机制。 首先通过类名生成Class类实例 在通过该Class 实例来创建(alloc)对象; 具体方法会用到 NSClassFromString(类名) 立即下载

相关推荐

IOS_反射机制的实例

由本群【天空air_as3_北京】分享的关于反射理论的Demo 可以帮助理解反射的用法和原理

在iOS开发中使用反射减少一些代码工作

已经很久没更新博客了。最近在一个iOS项目中,利用到了Obj-C语言的运行时反射特性,来减少一些代码编写的工作量,特记录下来。移动互联网下iOS客户端的开发,一般都会与服务端进行通讯,也会使用到Sqlite数据库来保存一些数据,按常规的搞法,一般都需要手动建表结构,写实体类对象,然后写插入、更新、查询等语句来实现功能,因此想到是否有一种通用的办法来进行一些代码方面的减负工作。通过这个项目的实践,已...

weixin_30877227的博客 221

Vitis Model Composer闪退?手把手教你用命令行启动Vivado 2021后的System Generator

本文详细解析了Vitis Model Composer闪退问题的根本原因,并提供了命令行启动Vivado 2021后System Generator的完整解决方案。从环境变量配置、组件缺失排查到权限问题处理,手把手教你通过命令行深度排错,并分享高级自动化脚本和系统优化技巧,帮助开发者高效使用Vitis Model Composer与System Generator进行HDL开发。

weixin_26905487的博客 347

iOS动态库实现插件化

系统在加载动态库时,会检查Framework的签名,签名中必须包含TeamIdentifier,并且Framework和主App的TeamIdentifier必须一致,目前而言,Apple并不希望开发者绕过App Store来更新App,因此需谨慎对待插件化的使用,对于不需要上架的企业级应用,是可以使用的。即可下载,如果使用真机测试,在同一网络下面,可将localhost缓存Mac的网路IP地址即可。将之前编译好的动态库放入服务器方便下载,然后在项目中下载并使用。将之前下载好的动态库加载到内存。

feelinghappy的专栏 1091

从零开始,手把手教你用RTL代码设计一个简单的8位CPU(附状态图与ALU设计)

本文详细介绍了如何从零开始设计一个简单的8位CPU,包括状态机设计、ALU实现与RTL编码实战。通过具体的指令集分析、状态图设计和Verilog代码示例,帮助读者深入理解CPU工作原理,并掌握RTL代码编写技巧。特别适合计算机系统学习者和硬件设计爱好者。

weixin_30847063的博客 320

为什么桥式传感器(Bridge Sensor)需要用到仪表放大器?

仪表放大器在桥式传感器的应用里起到关键作用,本文介绍桥式传感器的原理、特性,以及信号调理过程,并附上了一个 TINA - TI 的仿真电路。

疯狂的运放 1526

IOS 反射机制

http://www.2cto.com/kf/201306/219452.html 

weixin_34357267的博客 151

两个独立的正态分布的和仍然为正态分布的证明

正态分布的和

weixin_40064300的博客 1万+

iOS反射机制

iOS属性反射:说白了,就是将两个对象的所有属性,用动态的方式取出来,并根据属性名,自动绑值。(注意:对象的类,如果是派生类,就得靠其他方式来实现了,因为得到不该基类的属性。) 本人常用的反射方式,有如下两种: 从一个自定义实体类->自定义实体类从一个NSDictionary->自定义实体类(此方式最最常用,如网络Json数据会组成NSDictionary。sqlite查询数据,可以用

1万+

华为防火墙 策略路由实现多ISP接入Internet

PC3上网访问Server1时走ISP1。PC4上网访问Server1时走ISP2。1、配置接口IP并加入相应的安全区域。二、ISP2 运营商 R2路由器。一、ISP1 运营商 R1路由器。

一直被模仿,从未被超越 3014

PCIe协议之 Equalization篇 之 谈谈几种模式

对于两端有能力最高支持 32 GT/s 及以上速率的设备,如果把目标速度设置为 8 GT/s 或 16 GT/s 速率,是不建议采用 Bypass-Equalization-to-Highest-Rate 模式的,即便采用了,也不会针对 8 GT/s 或 16 GT/s 的目标速率进行 EQ。对于支持 32 GT/s 及以上速率的设备,其可能根本无需工作在 8 GT/s 或 16 GT/s 速率,这时再针对 8 GT/s 或 16 GT/s 速率进行 EQ,多少有点浪费。

那么菜的博客 561

iOS-反射机制和应用

反射机制 iOS反射机制:运行时选择创建哪个实例,并动态选择调用哪个方法。 获取Class的三种方法 // 通过字符串获取class Class class = NSClassFromString(@"NSString"); NSLog(@"class type : %@", class); // 直接用class 来创建对象 ,通过对象来获取class id str = [[class alloc] init]; NSLog(@"%@", [str clas...

iOS轰炸机的博客 521

SAP-MM-采购过账接口(与WMS集成)-代码

1、判断传入值非空检验; 2、判断过账日期是否允许在当前期间; 3、判断是否为集采订单业务,是否审批完成; 4、判断是否发布标准成本发布(此业务场景与价格控制3+S有关) 5、保存日志表; 6、判断业务公司订单审批状态; 7、创建STO交货单、集采公司订单过账、STO公司间创建交货单、交货单过账(两步法)、过账失败回滚(冲销交货、删除交货单、冲销集采入库)

qq_32198063的博客 420

【iOS开发系列】用简单工厂模式理解OC反射机制

// 在iOS开发中,简单工厂模式使用得并不多。但是、我认为这是OC反射机制很好的一个例子, // 所以本文将以计算器为例,讲解简单工厂模式和OC的反射机制。 // 【简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类( // 这些产品类继承自一个父类或接口)的实例。该模式中包含的角色及其职责:工厂角色、抽 // 象产品角色、具体产品角色】 //

BOOL 1099

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ios 反射机制--获取类的所有属性名

- (NSArray*)propertyKeys {          unsigned int outCount, i;          objc_property_t *properties = class_copyPropertyList([LJModel class], &outCount);          NSMutableArray *keys = [[NSMu

专业IOS技术 989

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

iOS反射机制: objc_property_t的使用

iOS反射机制: objc_property_t的使用 objc_property_t在实际开发中用得还是比较多的,例如需要获取一个注册类的属性,或者判断属性的类型,再或者获取属性之后自动给其赋值等等这里举个获取注册类的所有属性的例子,代码如下://获取类的所有属性名 - (NSArray*)getPropertieNamesByObject:(id)object { unsig...

九天揽月 459

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

iOS:反射机制与kvc

一、kvc kvc键值编码:通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。 这样就可以在运行时动态地访问和修改对象的属性。iOS开发中的黑魔法之一。 Animal *cat= [[Animal alloc] init]; [cat setValue:@"tom" forKey:@"name"]; 当调用[cat setValue:@"tom" forKey:@"name"];时,底层的执行机制如下: 程序优先调用setName:属性

_ 670

从JSONModel看Objective-C的反射机制

移动互联时代,JSON作为一种数据传输格式几乎随处可见。作为iOS开发者,收到一串JSON字符串要怎么处理?我想多数情况下是需要将它转成自定义的NSObject对象再使用,对于这个转换的过程,大部分人是这么做的: 1 2 3 4 5 6 7 NSDictionary* json = (fetch from Internet) ... User* user=[[

yongyinmg的专栏 6094

类比Spring框架来实现OC中的依赖注入

如果你之前使用过JavaEE开发中的Spring框架的话,那么你一定对依赖注入并不陌生。依赖注入(DI: Dependency Injection)是控制反转(IoC: Inversion of Control)的实现方式之一,另外一种是依赖查找(DL: Dependency Lookup)。当然在Spring框架中主要使用到了控制反转中的依赖注入这种方式。当然在Spring框架中除了依赖注入

tmchongye的专栏 2990

iOS应用插件开发的思路

做iOS开发已经两年多了。最近,在整理以前的代码,找到当时卡壳的一些代码片段,写博客跟大家分享一下。欢迎iOS初学者订阅我的博客,有什么问题可以留言交流。以下正文: 只说下简单的思路,具体实现就不在这里说了。应用内应该有个数据库,用于存储插件的基本信息,包括插件名,插件的库名,插件的入口类名之类的,这些数据来源于服务器端,可随时更新。将插件模块生成dylib文件利用dlopen()或者f

用了十年的昵称BadBoy 1854

2019年iOS进阶面试题总结(一)

准备找工作的你,可以看看,复习复习!! 1、说一下OC的反射机制 在动态运行下我们可以构建任何一个类,然后我们通过这个类知道这个类的所有的属性和方法,并且如果我们创建一个对象,我们也可以通过对象找到这个类的任意一个方法,这就是反射机制。 比如NSClassFormString,NSStringFormSelector,NSSelectorFormString参考链接 2、block的本质是什...

XiaoSiJun的博客 4611

ios 运行时

今天跟大家分享一下ios开发中的反射机制,

我的征途是星辰大海! 756

iOS反射机制(转)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33882443的博客 91

如何用AScript在3分钟内实现iOS应用动态热更新

AScript是一个用ActionScript3.0实现的轻量级解释型脚本语言,专为iOS应用动态热更新而设计。这款嵌入式脚本语言能够与ActionScript3.0无缝集成,无需重新编译即可执行大部分AS3内置类和自定义类,为移动应用开发提供了灵活的动态逻辑配置能力。 ## 核心理念:为什么选择AScript作为动态脚本语言 在iOS应用开发中,每次功能更新都需要重新提交App Store审

gitblog_01190的博客 159
上一篇: NSRunLoop
下一篇: iOS7新特性
focusjava
博客等级 码龄14年 127粉丝 528原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值