Objective-C RunTime学习记录

简介

  • OC作为一门动态语言,它会把一些决定工作从编译推迟到运行时,这个时候就需要一个运行时系统来处理编译之后的代码,这就是runtime存在的意义.它是OC运行框架的一块基石.

  • 我们可以在命令行中执行一下 xcrun -sdk iphonesimulator10.2 clang -rewrite-objc 命令(模拟器版本根据安装的版本来)编译一下某个OC文件,例如 ViewController.m

编译前
#import "ViewController.h"
#import "Person.h"

@interface ViewController ()

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[Person alloc] init];
    [p eat];  
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end
编译后
// @interface ViewController ()
/* @end */
// @implementation ViewController

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

    Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
    ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("eat"));

}

static void _I_ViewController_didReceiveMemoryWarning(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("didReceiveMemoryWarning"));

}
// @end

我们可以看到,OC中的方法调用都被转换成了objc_msgSend方法,也就是消息发送.

Runtime基础数据结构

objc_msgSend

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

id

typedef struct objc_object *id;
id是一个指向objc_object结构体的指针

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

typedef struct objc_class *Class;
objc_object结构体中有一个isa指针,指向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;

objc_class结构体中包含了:

  1. isa:元类指针
  2. super_class:父类指针
  3. name:类名
  4. version:类版本信息
  5. info:供运行时使用的一些位标识
  6. instance_size:类实例大小
  7. ivars:成员变量列表,存储Ivar类型
  8. methodLists:方法列表,存储Method类型
  9. cache:缓存
  10. protocols:协议列表
SEL

typedef struct objc_selector *SEL;
可以理解为方法名.我们可以用@selector()或者sel_registerName函数来获取.

Method

表示类中某个方法的类型
typedef struct objc_method *Method;

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
} 
  • method_name 方法名
  • method_types 存储方法参数类型和返回值类型
  • method_imp 函数指针
Ivar

表示类中成员变量的类型
typedef struct objc_ivar *Ivar;

struct objc_ivar {
    char *ivar_name                                          OBJC2_UNAVAILABLE;
    char *ivar_type                                          OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}
  • ivar_name 变量名
  • ivar_type 变量类型
  • ivar_offset 偏移量
  • space 占用空间
IMP

指向方法实现的函数指针

#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id (*IMP)(id, SEL, ...); 
#endif
objc_property_t

表示类属性的类型
typedef struct objc_property *objc_property_t;

Runtime操作函数

类常用操作函数

  • 获取类名 OBJC_EXPORT const char *class_getName(Class cls)
  • 判断是否是元类 OBJC_EXPORT BOOL class_isMetaClass(Class cls)
  • 获取父类 OBJC_EXPORT Class class_getSuperclass(Class cls)
  • 获取类指定名字成员变量(可以获取到私有变量) OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name)
  • 获取类有所成员变量列表 OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
  • 获取类指定名字实例方法(可以获取到私有方法和父类方法) OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
  • 获取类指定名字类方法(可以获取到私有方法和父类方法) OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
  • 获取类指定方法名方法函数指针(可以获取到私有方法) OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
  • 判断类是否能响应方法 OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel)
  • 获取类方法列表 OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount)
  • 判断是否遵循协议 OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
  • 获取类协议列表 OBJC_EXPORT Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)
  • 获取类指定名字属性 OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
  • 获取类属性列表 OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
  • 类添加方法 OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
  • 方法替换 OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp,const char *types)
  • 添加成员变量(不能在已存在的类中添加) OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size,uint8_t alignment, const char *types)
  • 添加协议 OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol)
  • 添加属性 OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)

objc操作函数

  • 创建一个类和元类OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name,size_t extraBytes)
  • 注册由objc_allocateClassPair创建的类 OBJC_EXPORT void objc_registerClassPair(Class cls)
  • 关联一个对象 OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
  • 获取关联对象 OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
  • 移除所有关联对象 OBJC_EXPORT void objc_removeAssociatedObjects(id object)

Method操作函数

  • 获取函数名OBJC_EXPORT SEL method_getName(Method m) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
  • 获取函数指针 OBJC_EXPORT IMP method_getImplementation(Method m) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
  • 获取方法类型 OBJC_EXPORT const char *method_getTypeEncoding(Method m) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
  • 指定函数指针 OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
  • 交换函数指针 OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

示例代码

  • 利用runtime简单实现字典转模型
const char *propertyListKey = "PropertyListKey";
+ (NSArray *)getAllPropertyLists {
    
    //获取关联对象
    NSArray *proList = objc_getAssociatedObject(self, propertyListKey);
    
    if (proList) {
        return proList;
    }
    
    //获取类属性列表
    unsigned int outCount;
    
    objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
    
    NSMutableArray *array = [NSMutableArray array];
    
    for (unsigned int i = 0; i < outCount; i++) {
        
        const char *propertyName = property_getName(propertyList[i]);
        //转换成oc字符串
        NSString *ocStr = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
        [array addObject:ocStr];
        
    }
    
    //设置关联对象             源对象    关键字             关联的对象            关键策略
    objc_setAssociatedObject(self, propertyListKey, array.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    free(propertyList);
    
    return array.copy;
}

+ (instancetype)dicChangeYXModel:(NSDictionary *)dic {
    
    id objc = [[self alloc] init];
    
    NSArray *propertyList = [self getAllPropertyLists];
    //遍历字典
    [dic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        
        //判断字典中的key是否有模型中对应的属性
        if ([propertyList containsObject:key]) {
            [objc setValue:obj forKey:key];
        }
        
    }];
    
    return objc;
}

  • 利用runtime简单实现反序列化
+ (void)archiveDataWithClassCoder:(NSCoder *)aCoder {
    
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {
        
        const char *str = ivar_getName(ivars[i]);
        NSString *name = [NSString stringWithUTF8String:str];
        [aCoder encodeObject:[self valueForKey:name] forKey:name];
        
    }
    free(ivars);
    
}

+ (void)unArchiveDataWithClassCoder:(NSCoder *)aDecoder {
    
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {
        
        const char *str = ivar_getName(ivars[i]);
        NSString *name = [NSString stringWithUTF8String:str];
        [self setValue:[aDecoder decodeObjectForKey:name] forKey:name];
        
    }
    free(ivars);
    
}
  • 利用runtime实现简单方法交换
+ (void)load {
    
    SEL originalSel = @selector(viewWillAppear:);
    SEL nowSel = @selector(yxLogViewDidLoad);
    
    Method originalMethod = class_getInstanceMethod([self class], originalSel);
    Method nowMethod = class_getInstanceMethod([self class], nowSel);
    
    if (class_addMethod([self class], originalSel, method_getImplementation(nowMethod), method_getTypeEncoding(nowMethod))) {
        
        class_replaceMethod([self class], nowSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        
    } else {
        
        method_exchangeImplementations(originalMethod, nowMethod);
        
    }
    
}

- (void)yxLogViewDidLoad {
    
    [self yxLogViewDidLoad];
    
    NSLog(@"%@",NSStringFromClass([self class]));
    
}
  • 一些自己学习runtime时测试的方法

ViewController.m

@interface ViewController ()

{
    NSString *asd;
}

@end

static const char *qweasd = "asddas";

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *p = [[Person alloc] init];
    [p eat];
    
    //获取类名
    const char * name = class_getName([self class]);
    NSLog(@"%s",name);
    //判断是否元类
    BOOL isMetaClass = class_isMetaClass(objc_getMetaClass(name));
    NSLog(@"是否是元类? %d  元类是%@",isMetaClass,objc_getMetaClass(name));
    //获取指定名字成员变量
    Ivar ivar = class_getInstanceVariable([Person class], "asd");
    NSLog(@"某个名字的成员变量%s",ivar_getName(ivar));
    //获取实例方法()可以获取私有方法
    Method method = class_getInstanceMethod([Person class], @selector(laugh));
    NSLog(@"实例方法名%@",NSStringFromSelector(method_getName(method)));
    //获取类方法
    Method classMethod = class_getClassMethod([Person class], @selector(imClassMethod));
    NSLog(@"类方法名%@",NSStringFromSelector(method_getName(classMethod)));
    //获取方法实现
    IMP strMethod = class_getMethodImplementation_stret([Person class], @selector(laugh));
    strMethod();
    
    IMP impClassMethod = class_getMethodImplementation(objc_getMetaClass(class_getName([Person class])), @selector(imClassMethod));
    impClassMethod();
    //判断类是否能响应方法,注意:类方法只能通过元类来寻找,所以返回为0
    NSLog(@"这个类知否能响应改方法? %d  %d",class_respondsToSelector([Person class], @selector(laugh)),class_respondsToSelector([Person class], @selector(imClassMethod)));
    //判断类是否遵循协议
    NSLog(@"这个类是否遵循协议? %d",class_conformsToProtocol([Person class], objc_getProtocol("TestProtocol")));
    //获取类属性
    objc_property_t pro = class_getProperty([Person class], "privatePro");
    NSLog(@"这个属性名字是 %s",property_getName(pro));
    
    //动态创建类
    Class mycls = objc_allocateClassPair([NSObject class], "MyNewClass", 0);
    //class_addIvar(<#__unsafe_unretained Class cls#>, <#const char *name#>, 占用大小, 对齐方式, 类型)
    if (class_addIvar(mycls, "myIvar", sizeof(NSString *), 0, "@")) {
        NSLog(@"添加成员变量成功");
    }
    if(class_addMethod(mycls, @selector(myClassMethod:), (IMP)myClassMethod, "v:i")){
        NSLog(@"添加方法成功");
    }
    
    objc_registerClassPair(mycls);
    
    id myObj = [[mycls alloc] init];
    
    NSString *shiyixia = @"呵呵";
    objc_setAssociatedObject(myObj, qweasd, shiyixia, OBJC_ASSOCIATION_RETAIN);
    [myObj setValue:@"asddsa" forKey:@"myIvar"];
    [myObj performSelector:@selector(myClassMethod:) withObject:@10];
    NSLog(@"%@",[myObj valueForKey:@"myIvar"]);
    NSString *asd = objc_getAssociatedObject(myObj, qweasd);
    NSLog(@"%@",asd);
//    [myObj myClassMethod:10];
    
    //关联属性
    [NSObject setMyname:@"啦啦"];
    NSLog(@"%@",[NSObject myname]);
    
    /*
     常用attribute    name    value
     nonatomic  "N" ""
     strong/retain  "&" ""
     weak   "W" ""
     属性的类型type  "T" "@TypeName", eg"@\"NSString\""
     属性对应的实例变量Ivar  "V" "Ivar_name", eg "_name"
     readonly   "R" ""
     getter "G" "GetterName", eg"isRight"
     setter "S" "SetterName", eg"setName"
     assign/atomic  默认即为assign和retain
     */

    objc_property_attribute_t proType = {"T","@\"NSString\""};
    objc_property_attribute_t proVisit = {"C",""};
    objc_property_attribute_t proIvar = {"V","_myProperty"};
    objc_property_attribute_t attri[] = {proType,proVisit,proIvar};
    
    //添加属性
    if (class_addProperty([Person class], "myProperty", attri, 3)) {
        NSLog(@"添加属性成功");
    }
    
    //实现setget方法
    class_addMethod([Person class], @selector(myPropertyM), (IMP)myProperty, "@:");
    class_addMethod([Person class], @selector(setMyPropertyM:), (IMP)setMyProperty, "v:@");
    NSLog(@"获取属性 %@", [p myPropertyM]);
    [p setMyPropertyM:@"wowowow"];
    NSLog(@"获取属性 %@", [p myPropertyM]);
    
}

//- (void)myClassMethod:(int)asd {
//    //这个方法不会实现,但是需要写(如果是直接调用的话,否则会崩溃)
//    
//}

void myClassMethod(id self2,SEL _cmd,NSNumber *a) {
    
    NSLog(@"传过来的值是: %@",a);
    Ivar ivar = class_getInstanceVariable([self2 class], "myIvar");
    NSString *ads = object_getIvar(objc_getClass("MyNewClass"),ivar);
    ads = @"haha";
    NSLog(@"%@,%s,%s",ads,ivar_getName(ivar),ivar_getTypeEncoding(ivar));
    
}

NSString * myProperty(id self1,SEL _cmd) {
    
    Ivar ivar = class_getInstanceVariable([self1 class], "_myProperty");
    return object_getIvar(self1, ivar);
}

void setMyProperty(id self1,SEL _cmd,NSString *str) {
    Ivar ivar = class_getInstanceVariable([self1 class], "_myProperty");
    object_setIvar(self1, ivar, str);
    
}

Person.h

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

- (void)eat;
- (NSString *)myPropertyM;
- (void)setMyPropertyM:(NSString *)str;

@end

Person.m

@interface Person ()
{
    NSString *asd;
    NSString *_myProperty;
}

@property (nonatomic, copy) NSString *privatePro;

@end

@implementation Person

- (void)eat {
    NSLog(@"吃呀吃");
}

- (void)laugh {
    NSLog(@"哈哈");
}

+ (void)imClassMethod {
    NSLog(@"我是私有类方法");
}

NSObject+test.h 类别添加属性

@interface NSObject (test)

@property (nonatomic, copy) NSString *myname;
+ (NSString *)myname;
+ (void)setMyname:(NSString *)myname;
@end

NSObject+test.m

const char *mynameV;

@implementation NSObject (test)

+ (NSString *)myname {
    return objc_getAssociatedObject(self, mynameV);
}

+ (void)setMyname:(NSString *)myname {
    objc_setAssociatedObject(self, mynameV, myname, OBJC_ASSOCIATION_RETAIN);
}

@end

你可能感兴趣的:(Objective-C RunTime学习记录)