码迷,mamicode.com
首页 > 其他好文 > 详细

runtime基础知识

时间:2016-07-07 22:36:52      阅读:268      评论:0      收藏:0      [点我收藏+]

标签:

Runtime数据结构:在Objective-C中,使用[receiver message]语法并不会马上执行receiver对象的message方法的代码,而是向receiver发送一条message消息,这条消息可能由receiver来处理,也可能由转发给其他对象来处理,也有可能假装没有接收到这条消息而没有处理。其实[receiver message]被编译器转化为:

id objc_msgSend ( id self, SEL op, ... );

 下面从两个数据结构idSEL来逐步分析和理解Runtime有哪些重要的数据结构。

SEL:

SEL是函数objc_msgSend第二个参数的数据类型,表示方法选择器,按照下面路径查看到SEL数据结构如下:

技术分享

typedef struct objc_selector *SEL;

     其实它就是映射到方法的C字符串,你可以通过Objc编译器命令@selector()或者Runtime系统的sel_registerName函数来获取一个SEL类型的方法选择器。

      如果你知道selector对应的方法名是什么,可以通过NSString* NSStringFromSelector(SEL aSelector)方法将SEL转化为字符串,再用NSLog打印。

id:

接下来看objc_msgSend第一个参数的数据类型idid是通用类型指针,能够表示任何对象,按下面路径打开objc.h文件技术分享

查看到id数据结构如下:

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员,根据isa指针就可以顺藤摸瓜找到对象所属的类

注意:根据Apple的官方文档Key-Value Observing Implementation Details提及,key-value observing是使用isa-swizzling的技术实现的,isa指针在运行时被修改,指向一个中间类而不是真正的类。所以,你不应该使用isa指针来确定类的关系,而是使用class方法来确定实例对象的类。

Class:

isa指针的数据类型是ClassClass表示对象所属的类,按下面路径打开objc.h文件

技术分享

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

可以查看到Class其实就是一个objc_class结构体指针,但这个头文件找不到它的定义,需要在runtime.h才能找到objc_class结构体的定义。

按下面路径打开runtime.h文件技术分享

查看到objc_class结构体定义如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *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 *` */

 

 

 


 

 

 

 

当我们对一个实例发送消息时(-开头的方法),会在该 instance 对应的类的 methodLists 里查找。

当我们对一个类发送消息时(+开头的方法),会在该类的 MetaClass 的 methodLists 里查找。

 创建person对象

    Person *p = [[Person alloc] init];

 调用对象方法

    [p run]; // 本质:让对象发送消息:objc_msgSend(p, @selector(run));

 调用类方法的方式:两种

第一种通过类名调用 :[Person run];

第二种通过类对象调用 :[[Person class] run];

 用类名调用类方法,底层会自动把类名转换成类对象调用

本质:让类对象发送消息 objc_msgSend([Person class], @selector(eat));

 

下面通过实例学习runtime

1、交换两个方法的实现

/*

     获得类方法:

     Method class_getClassMethod(Class cls , SEL name)

     获得某个类的实例对象方法

     Method class_getInstanceMethod(Class cls , SEL name)

     交换两个方法的实现

     void method_exchangeImplementations(Method m1 , Method m2)

     */

//类方法
Method class1 = class_getClassMethod([Person class], @selector(run)); Method class2 = class_getClassMethod([Person class], @selector(study)); method_exchangeImplementations(class1, class2); [Person run]; //调用study方法实现 [Person study];//调用run方法实现

//对象方法 Method instance1
= class_getInstanceMethod([Person class], @selector(run)); Method instance2 = class_getInstanceMethod([Person class], @selector(stydy)); method_exchangeImplementations(instance1, instance2); Person *person = [[Person alloc]init]; [person run];//调用study实现 [person stydy];//调用run实现

 2、用自己的方法替换系统方法

//创建UIImage的分类
//.h文件
#import
<UIKit/UIKit.h> @interface UIImage (Category)
+ (UIImage *)CZC_imageNamed:(NSString *)name; @end
//.m文件 #import "UIImage+Category.h" #import <objc/runtime.h> @implementation UIImage (Category) + (void)load { // 获取两个类的类方法 Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:)); Method m2 = class_getClassMethod([UIImage class], @selector(CZC_imageNamed:)); // 开始交换方法实现 method_exchangeImplementations(m1, m2); } + (UIImage *)CZC_imageNamed:(NSString *)name{ double version = [[UIDevice currentDevice].systemVersion doubleValue]; if (version >= 7.0) {
// 如果系统版本是7.0以上,使用另外一套文件名结尾是‘_os7’的扁平化图片 name = [name stringByAppendingString:@"_7"]; } return [UIImage CZC_imageNamed:name]; } UIImageView *imageVIew = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
//最终调用的图片是ios_7 imageVIew.image
= [UIImage imageNamed:@"ios"]; [self.view addSubview:imageVIew];

 3、在分类中设置属性,给任何一个对象设置属性

/* set方法,将值value 跟对象object 关联起来(将值value 存储到对象object 中)

 参数 object:给哪个对象设置属性

 参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节

 参数 value:给属性设置的值

 参数policy:存储策略 (assign 、copy 、 retain就是strong)

 设置值:

 void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

 获得值:

 id objc_getAssociatedObject(id object , const void *key)

 */
//Person分类
//.h文件
#import
"Person.h" @interface Person (Category)<NSCoding> @property (nonatomic, copy)NSString *name; @end
//.m文件 #import "Person+Category.h" #import <objc/runtime.h> char nameKey; @implementation Person (Category) - (void)setName:(NSString *)name{ objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY); } - (NSString *)name{ return objc_getAssociatedObject(self, &nameKey); } @end

4、 获得对象的所有成员变量

//创建Person类
#import
<Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface Person : NSObject<NSCoding> { NSString *_userName; NSUInteger _userAge; int _uerHeight; CGFloat _userMoney; CGFloat _userWeight; } @property (nonatomic, copy)NSString *name; @property (nonatomic, copy)NSString *age; //创建Person的分类
//.h文件
#import "Person.h" @interface Person (Category)<NSCoding> - (void)getAllIvar; @end //.m文件 #import "Person+Category.h" #import <objc/runtime.h> @implementation Person (Category) - (void)getAllIvar{ unsigned int outCount = 0; Ivar *ivars = class_copyIvarList([Person class], &outCount); // 遍历所有成员变量 for (int i = 0; i < outCount; i++) { // 取出i位置对应的成员变量 Ivar ivar = ivars[i]; const char *name = ivar_getName(ivar); const char *type = ivar_getTypeEncoding(ivar); NSLog(@"成员变量名:%s 成员变量类型:%s",name,type); } // 注意释放内存! free(ivars); }
//在controller中调用 [person getAllIvar];

5、利用runtime 获取所有属性来进行字典转模型

//创建Teacher类,其中包括Dog对象
//.h文件
#import
<Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "Dog.h" @interface Teacher : NSObject @property (nonatomic, copy)NSString *name; @property (nonatomic, copy)NSString *secondName; @property (nonatomic, assign)NSUInteger age; @property (nonatomic, assign)NSUInteger height; @property (nonatomic, assign)CGFloat money; @property (nonatomic, strong)Dog *dog; //当字典的key和模型的属性匹配不上 - (void)setDict:(NSDictionary *)dict; - (void)sendMessage:(NSString *)word; @end //.m文件 Teacher #import "Teacher.h" #import <objc/runtime.h> @implementation Teacher - (void)setDict:(NSDictionary *)dict { Class c = self.class; while (c &&c != [NSObject class]) { unsigned int outCount = 0; Ivar *ivars = class_copyIvarList(c, &outCount); for (int i = 0; i < outCount; i++) { Ivar ivar = ivars[i]; NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 成员变量名转为属性名(去掉下划线 _ ) key = [key substringFromIndex:1]; // 取出字典的值 id value = dict[key]; // 如果模型属性数量大于字典键值对数理,模型属性会被赋值为nil而报错 if (value == nil) continue; // 将字典中的值设置到模型上 [self setValue:value forKeyPath:key]; } free(ivars); c = [c superclass]; } } @end //创建Dog类
//.h文件 #import <Foundation/Foundation.h> @interface Dog : NSObject @property (nonatomic, copy)NSString *name; @property (nonatomic, copy)NSString *color; @property (nonatomic, assign)NSUInteger age; @end //在controller中调用 NSDictionary *dict = @{ @"name":@"夏明", @"age":@33, @"money":@1000 }; //字典赋值模型 Teacher *teacher = [[Teacher alloc]init]; [teacher setDict:dict]; NSLog(@"------teacher---%@", teacher);
//两层模型 NSDictionary
*dict2 = @{ @"name" : @"小明", @"age" : @40, @"dog" : @{ @"name":@"黑子", @"color":@"" } }; Teacher *teacher2 = [[Teacher alloc]init]; [teacher2 setObjDict:dict2]; NSLog(@"----teacher2-----%@", teacher2);

6、动态添加一个方法

//创建Person类
//.h文件
#import
<Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface Person : NSObject<NSCoding> @end
//.m文件Person #import "Person.h" #import "NSObject+Category.h" #import <objc/runtime.h> @implementation Person // void(*)() // 默认方法都有两个隐式参数, void eat(id self,SEL sel) { NSLog(@"%@ %@",self,NSStringFromSelector(sel)); } // 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来. // 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法 + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(eat)) { // 动态添加eat方法 // 第一个参数:给哪个类添加方法 // 第二个参数:添加方法的方法编号 // 第三个参数:添加方法的函数实现(函数地址) // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd class_addMethod(self, sel, eat, "v@:");//参数:eat可用(IMP)eat替换 } return [super resolveInstanceMethod:sel]; } @end //在controller中调用 //动态添加一个方法,p中没有这个方法
Person *p = [[Person alloc]init]; [p performSelector:@selector(eat)];
//第二种方式
+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(sendMessage:)) { class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *word) { NSLog(@"method resolution way : send message = %@", word); }), "v@*"); } return YES; } //动态添加方法, Person *p1 = [[Person alloc]init]; [p1 sendMessage:@"陈志超"];

7、消息的转发

//创建Teacher类
//.h
#import
<Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "Dog.h" @interface Teacher : NSObject @end //.m #import "Teacher.h" #import <objc/runtime.h> @implementation Teacher
/* 实现方式一
//消息转发,让另一个类对象执行方法 - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(sendMessage:)) { return [[Dog alloc]init]; } return nil; } */
//下面两个方式结合为方式二 #pragma mark - Normal Forwarding - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector]; if (!methodSignature) { methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"]; } return methodSignature; } - (void)forwardInvocation:(NSInvocation *)anInvocation { Dog *messageForwarding = [[Dog alloc]init]; if ([messageForwarding respondsToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:messageForwarding]; } } @end //创建Dog类 #import <Foundation/Foundation.h> @interface Dog : NSObject - (void)sendMessage:(NSString *)word; @end #import "Dog.h" @implementation Dog - (void)sendMessage:(NSString *)word{ NSLog(@"Dog--sendMessage = %@", word); } @end

//此时在controller中,
Teacher *t = [[Teacher alloc] init];
//下面代码会调用Dog的sendMessage方法
[t sendMessage:@"你好,teacher"];

 

runtime基础知识

标签:

原文地址:http://www.cnblogs.com/czc-wjm/p/5651633.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!