大家知道OC是一门动态语言,他将一些静态语言以及库的链接的一些事情放在运行时来进行处理,这就给我们增加了编程的灵活性,比如可以随意交换两个方法的执行,消息的转发等等。这就是Runtime运行时。这个库全部由C语言来编写的,该对象用结构体来表示,方法使用C函数来表示。当这些结构体以及方法用runtime函数来修饰封装后,我们就可以对类和对象进行创建、检查、修改等操作了。
1、基本结构
/// An opaque type that rePResents a method in a class definition.类中定义的方法
typedefstruct objc_method *Method;
/// An opaque type that represents an instance variable.类中定义的实例变量
typedefstruct objc_ivar *Ivar;
/// An opaque type that represents a category.类别
typedefstruct objc_category *Category;
/// An opaque type that represents an Objective-C declared property.类中声明的属性
typedefstruct objc_property *objc_property_t;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; isa指针,通过它可以找到对象所述的类,isa指针在代码运行时并不总是指向实例所属的类,因此不能通过其获取类型,但是可以通过‘’-class来确定。KVO的实现机理就是通过isa指针指向一个中间类而不是真实类来实现的。
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
constchar *name OBJC2_UNAVAILABLE; ------类名
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; ------属性列表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;------方法列表
struct objc_cache *cache OBJC2_UNAVAILABLE;------缓存,一种优化机制
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;------协议列表
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
2、常用的获取列表的属性unsigned int count; //获取属性列表 objc_property_t *propertyList = class_copyPropertyList([self class], &count); for (unsigned int i=0; i<count; i++) { const char *propertyName = property_getName(propertyList[i]); NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]); } //获取方法列表 Method *methodList = class_copyMethodList([self class], &count); for (unsigned int i; i<count; i++) { Method method = methodList[i]; NSLog(@"method---->%@", NSStringFromSelector(method_getName(method))); } //获取成员变量列表 Ivar *ivarList = class_copyIvarList([self class], &count); for (unsigned int i; i<count; i++) { Ivar myIvar = ivarList[i]; const char *ivarName = ivar_getName(myIvar); NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]); } //获取协议列表 __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count); for (unsigned int i; i<count; i++) { Protocol *myProtocal = protocolList[i]; const char *protocolName = protocol_getName(myProtocal); NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]); }3、消息转发在OC中调用方法一般是如下调用
id returnValue = [someObject messageName:parameter];,someObject是消息的接受者,messageName是一个选择器,parameter则为参数。选择器+参数 就是我们所称为的消息。
其在底层中是将我们的消息转换为标准的C语言函数,Void objc_msgSend(id self , SEL cmd , .....) ,self 为消息接收者,cmd为选择器,省略号为参数,表示可变长度参数。因此以上的消息方法会转换为id returnValue = objc_msgSend( someObject , @selector(messageName:) , parameter);
之所以Objc_msgsend方法总能找到正确的函数去执行,原因是OC中每个类都有一张方法的列表存储这个类的所有方法,当发出objc_msgsend时,会根据object的isa指针找到类结构的方法,如果找不到则会到父类寻找该方法的实现,直到NSObject类。上面有提到cach缓存机制,苹果为了加快寻找速率,runtime 系统会缓存使用过的SEL 和方法地址。
通过object的isa指针找到他的类结构class在在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行(IMP)如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行(IMP)如果还未找到则会去父类中重复上面操作寻找如果最终还是未找到,则会调用实例方法调用+(bool)resolveInstanceMethod:(SEL)sel ;类方法调用:+(bool)resolveClassMethod:(SEL)sel;如果返回为YES,则会重启一次消息发送过程,调用你自己添加的方法。+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel ==@selector(sendMessage:)) {
class_addMethod([selfclass], sel,imp_implementationWithBlock(^(idself,NSString *Word) {
NSLog(@"method resolution way : send message = %@", word);
}), "v@*");
}
returnYES;
}
- (void)sendMessage:(NSString *)word
{
NSLog(@"normal way : send message = %@", word);
}
如果返回NO,则会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,再次方法中可以将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target,- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector ==@selector(sendMessage:)) {
return [MessageForwardingnew];//新建的类,见下
}
returnnil;
}
#import <Foundation/Foundation.h>
@interface MessageForwarding :NSObject
- (void)sendMessage:(NSString *)word;
@end
如果return nil,则会- (void)forwardInvocation:(NSInvocation *)anInvocation将你调用的不存在的方法打包成NSInvocation
传给你。做完你自己的处理后,调用invokeWithTarget:
方法让某个target触发这个方法。- (void)forwardInvocation:(NSInvocation *)anInvocation
{
MessageForwarding *messageForwarding = [MessageForwardingnew];
if ([messageForwardingrespondsToSelector:anInvocation.selector]) {
[anInvocationinvokeWithTarget:messageForwarding];
}
}
#import <Foundation/Foundation.h>
@interface MessageForwarding :NSObject
- (void)sendMessage:(NSString *)word;
@end
4、runtime实际应用更改私有属性 、给category添加属性、归档解档、AOP等等,具体事例请看本博客其他文章
新闻热点
疑难解答