1.获取对象属性和变量列表
这里准备了一个Model类
#import
@interface UserModel : NSObject
{
NSString *userPhone;
}
@property (nonatomic, copy)NSString *userName;
@property (nonatomic, assign)BOOL userGender;
@property (nonatomic, assign)NSInteger userAge;
@end
在要查看它属性列表的类里引入头文件:
#import "UserModel.h"
#import
然后实现如下代码:
//getIvarsList
unsigned int ivarCount;//无符号int,储存范围比普通int大一倍
Ivar *ivars = class_copyIvarList([UserModel class], &ivarCount);
NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:ivarCount];
for (unsigned int i = 0; i < ivarCount; i++) {
Ivar ivar = ivars[i];
const char *type = ivar_getTypeEncoding(ivar);
const char *name = ivar_getName(ivar);
[propertiesArray addObject:[NSString stringWithUTF8String:name]];
NSLog(@"成员变量的类型 %s,名称 %s ",type, name);
}
free(ivars);//class_copyIvarList是C语言的实现,必须释放
NSLog(@"%@",propertiesArray);
可以用class_copyPropertyList替换class_copyIvarList,class_copyPropertyList只能获取类的属性,class_copyIvarList可以获取属性和成员变量
打印:
2018-04-03 11:29:36.989517+0800 PodsTest[1596:98852] 成员变量的类型 @"NSString",名称 userPhone
2018-04-03 11:29:36.989642+0800 PodsTest[1596:98852] 成员变量的类型 B,名称 _userGender
2018-04-03 11:29:36.989737+0800 PodsTest[1596:98852] 成员变量的类型 @"NSString",名称 _userName
2018-04-03 11:29:36.989817+0800 PodsTest[1596:98852] 成员变量的类型 q,名称 _userAge
2018-04-03 11:29:36.989963+0800 PodsTest[1596:98852] (
userPhone,
"_userGender",
"_userName",
"_userAge"
)
2.给分类添加属性
给刚才的UserModel类添加了一个分类:
#import "UserModel.h"
@interface UserModel (Property)
@property (nonatomic, copy)NSString *userPhone;
@end
导入#import "UserModel+Property.h"头文件运行一下:
UserModel *userModel = [[UserModel alloc]init];
userModel.userPhone = @"123";
NSLog(@"%@",userModel.userPhone);
崩溃了,报错如下:
2018-04-03 12:10:48.858169+0800 PodsTest[2273:172391] -[UserModel setUserPhone:]: unrecognized selector sent to instance 0x6000002207e0
2018-04-03 12:10:48.864333+0800 PodsTest[2273:172391] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UserModel setUserPhone:]: unrecognized selector sent to instance 0x6000002207e0'
没有setUserPhone方法的实现
在分类实现文件里加入set/get方法的实现:
#import "UserModel+Property.h"
#import
@implementation UserModel (Property)
- (void)setUserPhone:(NSString *)userPhone {
objc_setAssociatedObject(self, @"userPhone", userPhone, OBJC_ASSOCIATION_RETAIN);
}
- (NSString *)userPhone {
return objc_getAssociatedObject(self, @"userPhone");
}
@end
再运行:
2018-04-03 12:14:10.598727+0800 PodsTest[2358:178611] 123
成功打印
3.获取对象方法列表
给UserModel类添加几个方法:
#import "UserModel.h"
@implementation UserModel
- (void)run {
NSLog(@"%s",__FUNCTION__);
}
- (void)eat {
NSLog(@"%s",__FUNCTION__);
}
- (void)work {
NSLog(@"%s",__FUNCTION__);
}
@end
实现如下代码:
//getMethodsList
unsigned int methodCount;
Method *methods = class_copyMethodList([UserModel class], &methodCount);
NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:methodCount];
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
IMP imp = method_getImplementation(method);//方法调用地址
SEL name_f = method_getName(method);//方法
const char *name_s = sel_getName(method_getName(method));//方法名
int arguments = method_getNumberOfArguments(method);//参数个数
const char *encoding = method_getTypeEncoding(method);//编码方式
[methodsArray addObject:[NSString stringWithUTF8String:name_s]];
}
free(methods);
NSLog(@"%@",methodsArray);
打印结果:
2018-04-03 12:52:05.447052+0800 PodsTest[3250:245253] (
eat,
work,
run
)
如果类里有属性或成员变量,这里也会打印出set/get方法
4.访问和修改私有变量
在UserModel类里准备了私有属性和变量:
#import "UserModel.h"
@interface UserModel()
@property (nonatomic ,copy)NSString *firstName;
@end
@implementation UserModel
{
NSString *lastName;
}
- (instancetype)init {
self = [super init];
if (self) {
self.firstName = @"Nee";
lastName = @"Datou";
}
return self;
}
访问:
UserModel *userModel = [[UserModel alloc]init];
Ivar ivar1 = class_getInstanceVariable([UserModel class], "_firstName");
Ivar ivar2 = class_getInstanceVariable([UserModel class], "lastName");
NSString *ivarValue1 = object_getIvar(userModel, ivar1);
NSString *ivarValue2 = object_getIvar(userModel, ivar2);
NSLog(@"%@ %@",ivarValue1,ivarValue2);
修改:
object_setIvar(userModel, ivar1, @"first");
object_setIvar(userModel, ivar2, @"last");
NSString *ivarValueNew1 = object_getIvar(userModel, ivar1);
NSString *ivarValueNew2 = object_getIvar(userModel, ivar2);
NSLog(@"%@ %@",ivarValueNew1,ivarValueNew2);
打印结果:
2018-04-03 13:24:17.133980+0800 PodsTest[4096:306556] Nee Datou
2018-04-03 13:24:17.134103+0800 PodsTest[4096:306556] first last
5.动态归档/解档
当属性比较多的时候可以用runtime来遍历属性列表完成归档和解档的协议方法:
#import "UserModel.h"
#import
@implementation UserModel
//归档协议方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
// [aCoder encodeObject:self.userName forKey:@"name"];
// [aCoder encodeBool:self.userGender forKey:@"gender"];
// [aCoder encodeInteger:self.userAge forKey:@"age"];
unsigned int ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
for (unsigned i = 0; i < ivarCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
}
//解档协议方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
// if (self = [super init]) {
// self.userName = [aDecoder decodeObjectForKey:@"name"];
// self.userGender = [aDecoder decodeBoolForKey:@"gender"];
// self.userAge = [aDecoder decodeIntegerForKey:@"age"];
// }
// return self;
if (self = [super init]) {
unsigned int ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
for (unsigned i = 0; i < ivarCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
free(ivars);
}
return self;
}
@end
6.字典转模型
在NSObject分类里添加一个方法:
#import
@interface NSObject (Model)
+ (instancetype)initModelWithDict:(NSDictionary *)dict;
@end
利用runtime遍历属性列表实现字典到模型的转换:
#import "NSObject+Model.h"
#import
@implementation NSObject (Model)
+ (instancetype)initModelWithDict:(NSDictionary *)dict {
id obj = [[self alloc]init];
unsigned int count;
//获取属性列表
objc_property_t *propertys = class_copyPropertyList([self class], &count);
for (unsigned i = 0; i < count; i++) {
objc_property_t property = propertys[i];
//获取属性Key
NSString *propertyKey = [NSString stringWithUTF8String:property_getName(property)];
//从字典中获取属性Value
id propertyValue = dict[propertyKey];
if (propertyValue) {
[obj setValue:propertyValue forKey:propertyKey];
}
}
free(propertys);
return obj;
}
@end
调用:
NSDictionary *dic = @{@"userName":@"datou",@"userGender":@"1",@"userAge":@"16"};
UserModel *userModel = [UserModel initModelWithDict:dic];
NSLog(@"%@ %@ %@",userModel.userName,userModel.userGender,userModel.userAge);
打印结果:
2018-04-03 17:11:57.115482+0800 PodsTest[6977:525390] datou 1 16
这里只是一个简单的例子,对于更复杂的json,比如字典嵌套字典,数组嵌套字典,需要二级转换、三级转换
7.方法交换
我在UserModel类里添加了两个方法:
- (void)run {
NSLog(@"run!");
}
- (void)eat {
NSLog(@"eat!");
}
交换它们的方法实现:
//获取run的方法实现
Method method1 = class_getInstanceMethod([UserModel class], @selector(run));
//获取eat的方法实现
Method method2 = class_getInstanceMethod([UserModel class], @selector(eat));
//交换
method_exchangeImplementations(method1, method2);
UserModel *userModel = [[UserModel alloc]init];
[userModel performSelector:@selector(run)];
[userModel performSelector:@selector(eat)];
打印结果:
2018-04-03 17:42:15.590656+0800 PodsTest[7465:572622] eat!
2018-04-03 17:42:15.590785+0800 PodsTest[7465:572622] run!
8.动态添加方法
UserModel *userModel = [[UserModel alloc]init];
[userModel performSelector:@selector(work)];
当一个对象调用了一个未实现的方法就会进入下面这个类方法:
+ (BOOL)resolveInstanceMethod:(SEL)sel:
可以在这个类方法里动态添加方法实现:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(work)) {
// 第一个参数:给哪个类添加方法
// 第二个参数:添加方法的方法编号
// 第三个参数:添加方法的函数实现(函数地址)
// 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
class_addMethod([UserModel class], @selector(work), (IMP)workFunction, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void workFunction(id self, SEL _cmd) {
NSLog(@"work!");
}