项目终于忙完了 ,可以有点儿时间 学习一下 ,学习的runtime 记录一下 ,自己敲了一遍,感觉到更加亲切了点儿,话不多说。直接上代码了。
一 动态创建类对象
// 继承类 类名字 是否需要扩展
Class Person = objc_allocateClassPair([NSObject class], "Person", 0);
//添加成员变量
//类 、 名字 、 大小、 补齐方式 、 对应的编码(这个是苹果的一个列表)
class_addIvar(Person, "_age", sizeof(int), 1, @encode(int));
//添加方法
class_addMethod(Person, @selector(run), (IMP)run, "v@:");
//一定要再注册之前,把成员变量 或者属性加上
objc_registerClassPair(Person);
// 实例化对象 进行测试
id student = [[Person alloc] init];
[student setValue:@1 forKey:@"_age"];
NSLog(@"%@",[student valueForKey:@"_age"]);
二 查看对象的属性 和 成员变量
//查看成员变量
unsigned int number = 0;
Ivar *ivars = class_copyIvarList(Person, &number);
//查看属性
unsigned int count;
class_copyPropertyList(Person, &count);
大家一定对此也有疑问,感觉这两个也差不多 , 但是是有差别的,class_copyPropertyList 这个方法只能获取@property 声明的属性,然而 ivar 就可以获取所有的成员变量和属性 _age这类。
当然 获取完属性 我们就可以进行转换成 字符串
for (int i = 0; i
这样就可以转换成字符串了,这样就有了功能 ,
1.可以进行赋值 也就是model 避免了过多空间的浪费,有几个属性,就需要几个值
2.可以进行归档,解档,如下
-(void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int number = 0;
Ivar *ivarLists = class_copyIvarList([self class], &number);
for (int i = 0; i < number; i++) {
const char *name = ivar_getName(ivarLists[i]);
NSString *realName = [NSString stringWithUTF8String:name];
NSString *value = [self valueForKey:realName];
[aCoder encodeObject:value forKey:realName];
}
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder {
unsigned int number = 0;
Ivar *ivarLists = class_copyIvarList([self class], &number);
for (int i = 0; i < number; i++) {
const char *name = ivar_getName(ivarLists[i]);
NSString *realName = [NSString stringWithUTF8String:name];
NSString *value = [aDecoder decodeObjectForKey:realName];
[self setValue:value forKey:realName];
}
return self;
}
三 动态添加方法
当我们动态添加方法之后 ,因为没有实现方法 所以会走如下的方法, 在这个里面 我们进行拦截,告诉程序 实现方法的 imp指针在哪里, imp指针就是 实现方法的地址。(这个是实例对象的方法找不到调用的方法,还有一个是类的方法)
+(BOOL)resolveInstanceMethod:(SEL)sel {
if ([NSStringFromSelector(sel) isEqualToString:@"foo:"]) {
class_addMethod(self, sel, (IMP)haha, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
大家注意 这个格式 , 首先是 obj 就是对象, sel 是方法(就是你调用的方法,但是没有) objc 是参数。 只有遵守格式才能调用到这个方法
void haha(id obj,SEL sel,NSString * objc){
NSLog(@"%@--%@-- %@",obj,NSStringFromSelector(sel),objc);
}
四 方法交换 、替换
SEL mySel = @selector(eat);
SEL originSel = @selector(viewDidLoad);
Method originMethod = class_getInstanceMethod([self class], originSel);
Method newMethod = class_getInstanceMethod([self class], mySel);
交换方法
method_exchangeImplementations(myMethod, cusMethod);
方法替换
class_replaceMethod([self class], mySel, method_getImplementation(myMethod), method_getTypeEncoding(myMethod));
五。绑定对象
在刚开始开发的时候,碰见有很多 btn的实现方法都是block 方法实现,看了里面的代码 也不懂,当时毕竟是个菜菜---哈哈。
今天就解答一下 绑定的问题,一般是在set 和 get的时候用到的比较多:
在绑定的时候 调用该方法,此处掉了weak的 方法
objc_setAssociatedObject(self, (__bridge const void *)@"zsl", object, OBJC_ASSOCIATION_ASSIGN);
当需要取出的时候
id object = objc_getAssociatedObject(self, (__bridge const void *)@"zsl");
就比如 我们在扩展类中 给对象 创建了成员变量很方便
六 发送消息
oc代码的方法的调用 就是发送消息 就比如 我们要 alloc init 一个对象。
[[Person alloc] init];
转换成底层代码
Person * pOne = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
objc_msgSend(pOne, @selector(init));
七 关于kvo
关于kvo ,实质是在addObserver 的方法中 创建了该对象的子类,并且 对 self 进行了一个转类, 转成了子类,之后就是重写set 方法,
以下是自定义的监听
-(void)zsl_AddObserver:(NSObject *)object forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context Block:(Block)smallBlock {
NSString *myClassName = [NSString stringWithFormat:@"NewCar"];
Class myClass = objc_allocateClassPair([self class], myClassName.UTF8String, 0);
//完成注册
objc_registerClassPair(myClass);
//转类
object_setClass(self, myClass);
//重写 setname 方法
class_addMethod(myClass, @selector(setName:), (IMP)setName,"v@:@");
//绑定控制器 此处的 object 就是监听对象
objc_setAssociatedObject(self, (__bridge const void *)@"zsl", object, OBJC_ASSOCIATION_ASSIGN);
//绑定控制器 block 方式
objc_setAssociatedObject(self, (__bridge const void *)@"zsl_block", smallBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
下面就是 重写的set方法 ,回调方式有2中 一种是block 的 一种就是 调用方法的 Block中的参数 就是 回调中需要的参数
void setName(id self, SEL _cmd, NSString *newName) {
// NSLog(@"%@",newName);
struct objc_super Car = {self,class_getSuperclass([self class])};
objc_msgSendSuper(&Car, _cmd,newName);
id object = objc_getAssociatedObject(self, (__bridge const void *)@"zsl");
// objc_msgSend(object, @selector(observeValueForKeyPath:ofObject:change:context:), @"name",self,@{@"name":newName},nil);
Block block = (Block) objc_getAssociatedObject(self, (__bridge const void *)@"zsl_block");
block(@"name", object, @{@"name":newName});
}
仅是自己的学习记录,如果有错误之处,望指出,进行修改!!