动态给分类添加属性
1.创建UIGestureRecognizer的类目 UIGestureRecognizer+Block.h
#import "UIGestureRecognizer+Block.h"
#import <objc/runtime.h>
static const int target_key;
@implementation UIGestureRecognizer (Block)
+ (instancetype) yj_gesterRrecognizerWithAction:(YJBlock)block
{
__weak typeof(self) weakself = self;
return [[weakself alloc] initWithActionBlock:block];
}
- (instancetype) initWithActionBlock:(YJBlock)block
{
self = [self init];
[self addActionBlock:block];
[self addTarget:self action:@selector(invoke:)];
return self;
}
- (void) invoke:(id)sender
{
YJBlock block = objc_getAssociatedObject(self, &target_key);
if (block){
block(sender);
}
}
- (void) addActionBlock:(YJBlock)block
{
if (block){
objc_setAssociatedObject(self, &target_key, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- id object 给谁添加就是谁的对象
- const void *key 关联对象的key
- id value 被关联者(要添加的属性),这里是一个block()
- objc_AssociationPolicy policy : 关联时采用的协议,有assign,retain,copy等协议,一般使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
2.在创建的类中进行调用
UIView *viewM = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
viewM.backgroundColor = [UIColor grayColor];
[self.view addSubview:viewM];
[viewM addGestureRecognizer:[UITapGestureRecognizer yj_gesterRrecognizerWithAction:^(id gesterRecognizer) {
NSLog(@"点击了view");
}]];
方法的交换
1.创建UIImage的类目UIImage+hook.h
#import "UIImage+hook.h"
#import <objc/runtime.h>
@implementation UIImage (hook)
+(void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class selfClass = object_getClass([self class]);
SEL oriSEL = @selector(imageNamed:);
Method oriMethod = class_getInstanceMethod(selfClass, oriSEL);
SEL cusSEL = @selector(myImageNamed:);
Method cusMethod = class_getInstanceMethod(selfClass, cusSEL);
BOOL addSuccess = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
if (addSuccess){
class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else{
method_exchangeImplementations(oriMethod, cusMethod);
}
});
}
+(UIImage *)myImageNamed:(NSString *)name
{
NSString *newName = [NSString stringWithFormat:@"%@%@",@"new_",name];
return [self myImageNamed:newName];
}
@end
//替换方法
class_replaceMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
- 第一个参数 class: 给哪个类添加参数
- 第二个参数 SEL : 被替换的那个方法
- 第三个参数 IMP : A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
- 第四个参数 types :An array of characters that describe the types of the arguments to the method.
2.调用
UIImageView *subView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
//图片的实际名字是 new_Cart
[subView setImage:[UIImage imageNamed:@"Cart"]];
[self.view addSubview:subView];
字典转模型
1.创建NSObjec的类别 NSObject+hook.h
#import "NSObject+hook.h"
#import <objc/runtime.h>
@implementation NSObject (hook)
const char *kPeropertyListKey = "kkkkkPeropertyListKey";
+ (instancetype) modelWithdict:(NSDictionary *)dict
{
/* 实例化对象*/
id objc = [[self alloc] init];
/* 使用字典,设置对象信息*/
/* 1.或者self的属性列表*/
NSArray *propertyList = [self yj_PropertyList];
/* 2.遍历字典*/
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
/* 3.判断 key是否 propertyList中*/
if ([propertyList containsObject:key]){
/* 说明属性存在,可以使用kvc 设置数值*/
[objc setValue:obj forKey:key];
}
}];
/*返回对象*/
return objc;
}
- (NSArray *) yj_PropertyList
{
NSArray *ptyList = objc_getAssociatedObject(self, kPeropertyListKey);
/* 如果 ptyList有值,直接返回*/
if (ptyList){
return ptyList;
}
/* 调用运行时方法, 取得类的属性列表 */
/* 成员变量:
* class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount)
* 方法:
* class_copyMethodList(__unsafe_unretained Class cls, unsigned int *outCount)
* 属性:
* class_copyPropertyList(__unsafe_unretained Class cls, unsigned int *outCount)
* 协议:
* class_copyProtocolList(__unsafe_unretained Class cls, unsigned int *outCount)
*/
unsigned int outCount = 0;
/**
* 参数1: 要获取得类
* 参数2: 类属性的个数指针
* 返回值: 所有属性的数组, C 语言中,数组的名字,就是指向第一个元素的地址
*/
/* retain, creat, copy 需要release */
objc_property_t *properrtyList = class_copyPropertyList([self class], &outCount);
NSMutableArray *mtArray = [NSMutableArray array];
/* 遍历所有属性*/
for (unsigned int i = 0; i < outCount; i++) {
/* 从数组中取得属性 */
objc_property_t property = properrtyList[i];
/* 从 property 中获得属性名称 */
const char *propertyName_C = property_getName(property);
/* 将 C 字符串转化成 OC 字符串 */
NSString *propertyName_OC = [NSString stringWithCString:propertyName_C encoding:NSUTF8StringEncoding];
[mtArray addObject:propertyName_OC];
}
/* 设置关联对象 */
/**
* 参数1 : 对象self
* 参数2 : 动态添加属性的 key
* 参数3 : 动态添加属性值
* 参数4 : 对象的引用关系
*/
objc_setAssociatedObject(self, kPeropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
/* 释放 */
free(properrtyList);
return mtArray.copy;
}
@end
2.创建模型
#import <Foundation/Foundation.h>
#import "NSObject+hook.h"
@interface Model : NSObject
@property (copy,nonatomic) NSString *name;
@property (copy,nonatomic) NSString *sex;
@property (copy,nonatomic) NSString *age;
@end
3.调用
NSDictionary *dic = @{@"name":@"我爱NBA",
@"sex":@"男",
@"age":@25
};
Model *model = [Model modelWithdict:dic];
NSLog(@"name:%@ sex:%@ ",model.name,model.sex);
2018-02-28 09:50:32.343396+0800 RunTime使用场景[18298:1609962] name:我爱NBA sex:男
对私有属性进行修改
#pragma mark - 获取所有的属性(包括私有的)
- (void) getAllIvar
{
unsigned int count = 0;
//Ivar: 定义对象的实例变量,包括类型和名字
//获取所有的属性(包括私有的)
Ivar *ivars = class_copyIvarList([NSString class], &count);
for (int i = 0; i< count; i++) {
//去除成员变量
Ivar ivar = ivars[i];
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
NSLog(@"属性->%@ 和 %@",name,type);
}
}
- (void) getAllMethod
{
unsigned int count = 0;
Method *methodList = class_copyMethodList([UIPageControl class], &count);
for (int i = 0 ; i< count; i ++) {
Method method = methodList[i];
NSString *methodName = NSStringFromSelector(method_getName(method));
NSLog(@"方法名 ->%@",methodName);
}
}
归档解档
1.创建YYModel
#import <Foundation/Foundation.h>
@interface YYModel : NSObject <NSCoding>
@property(nonatomic,assign) NSInteger age;
@property(nonatomic,copy) NSString *name1;
@property(nonatomic,copy) NSString *name2;
@property(nonatomic,copy) NSString *name3;
@property(nonatomic,copy) NSString *name4;
@property(nonatomic,copy) NSString *name5;
@end
------------------------------.m文件
#import "YYModel.h"
#import <objc/runtime.h>
@implementation YYModel
//NSKeyedUnarchiver 从二进制流读取对象。
//NSKeyedArchiver 把对象写到二进制流中去。
// 读取实例变量,并把这些数据写到coder中去。序列化数据
//1)、如果是类 就用encodeObject: forKey:
//2)、如果是普通的数据类型就用 eg、encodeInt: forKey:
- (void)encodeWithCoder:(NSCoder *)aCoder
{
unsigned int count = 0;
//利用runtime获取实例变量的列表
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i =0; i< count; i++) {
//读出i位置对应的实例变量
Ivar ivar =ivarList[i];
//查看实例变量的名字
const char *name = ivar_getName(ivar);
//c语言字符串转化为nsstring
NSString *namestr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
//利用kvo取出属性对应的值
id value = [self valueForKey:namestr];
//归档
[aCoder encodeObject:value forKey:namestr];
}
//记住c语言copy出来的要进行释放
free(ivarList);
}
//从coder中读取数据,保存到相应的变量中,即反序列化数据
//1)、如果是类 就用decodeObjectForKey:
//2)、如果是普通的数据类型就用 eg、decodeIntForKey:
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0 ; i< count; i++) {
Ivar ivar = ivarList[i];
const char *name = ivar_getName(ivar);
NSString *namestr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
id value = [aDecoder decodeObjectForKey:namestr];
//设置到成员变量身上
[self setValue:value forKey:namestr];
}
free(ivarList);
}
return self;
}
@end
2.调用
YYModel *model = [[YYModel alloc] init];
model.age = 18;
model.name1 = @"胡航";
model.name2 = @"梁静茹";
model.name3 = @"女女生";
//创建路径
NSString *docunmentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSLog(@"docunmentPath路径:%@",docunmentPath);
NSString *filePath = [docunmentPath stringByAppendingString:@"/YYModel.data"];
//存储用户信息,归档
BOOL result = [NSKeyedArchiver archiveRootObject:model toFile:filePath];
if (result) {
NSLog(@"归档成功%@",filePath);
}else{
NSLog(@"归档失败");
}
YYModel *yy = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"年龄%@\nname1:%@\nname2:%@",@(yy.age),yy.name1,yy.name2);
动态添加方法
1.创建类dog
#import "Dog.h"
#import <objc/runtime.h>
@implementation Dog
// 默认方法都有两个隐式参数
void eat(id self,SEL sel){
NSLog(@"%@ %@",self,NSStringFromSelector(sel));
NSLog(@"动态添加了一个方法");
}
//当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来
//刚好可以用来判断,未实现的方法是不是我们想要添加动态添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == NSSelectorFromString(@"eat")) {
//注意:这里需要强转成IMP类型
class_addMethod(self, sel, (IMP)eat, "v@:");
return YES;
}
//先恢复,不然会覆盖系统的方法
return [super resolveInstanceMethod:sel];
}
@end
2.实现
Dog *dog = [[Dog alloc] init];
//默认dog,没有实现eat方法,可以通过performSelector调用,但是会报错
//动态添加方法就不会报错
[dog performSelector:@selector(eat)];