iOS Runtime

Objective-C是一门动态语言,编译后的代码还需要一个运行时系统才能执行。

我们写的代码方法调用[target doSomething];,实际上会被转化成Runtime的C代码执行objc_msgSend(target, @selector(doSomething));。


在Runtime中方法、实例变量、类别、属性、类的描述:

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

/// An opaque type that represents a category.
typedef struct objc_category *Category;

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

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 *` */

代码:

https://github.com/hkshenSmart/RuntimeLearn.git


//
//  Person.h
//  RuntimeLearn
//
//  Created by hkshen on 2017/9/12.
//  Copyright © 2017年 hkshen. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, assign) int age; // 属性

- (void)doWork;
- (void)doEat;

@end


//
//  Person.m
//  RuntimeLearn
//
//  Created by hkshen on 2017/9/12.
//  Copyright © 2017年 hkshen. All rights reserved.
//

#import "Person.h"

@interface Person ()

- (void)doSleep;

@end

@implementation Person {
    
    NSString *name; // 实例变量
}

//@synthesize age;
// 相当于@synthesize age = age; 不写@synthesize age; 实例变量_age;

- (instancetype)init {
    
    if (self = [super init]) {
        name = @"hkshen";
        self.age = 27;
    }
    return self;
}

- (void)doWork {
    
    NSLog(@"Work alive");
}

- (void)doEat {
    
    NSLog(@"Eat alive");
}

- (void)doSleep {
    
    NSLog(@"Sleep alive");
}

- (NSString *)description {
    
    return [NSString stringWithFormat:@"name:%@---age:%d", name, self.age];
}

@end


//
//  ViewController.m
//  RuntimeLearn
//
//  Created by hkshen on 2017/9/12.
//  Copyright © 2017年 hkshen. All rights reserved.
//

#import "ViewController.h"
#import "Person.h"
#import <objc/runtime.h>
#import "Person+Sex.h"

@interface ViewController () {
    
    Person *person;
}

- (IBAction)doGetAllVariable:(id)sender; // 获取变量
- (IBAction)doGetAllMethod:(id)sender; // 获取方法
- (IBAction)doChangeVariableValue:(id)sender; // 改变私有变量的值
- (IBAction)doAddMethod:(id)sender; // 添加新方法
- (IBAction)doAddVariable:(id)sender; // 添加新属性
- (IBAction)doReplaceMethod:(id)sender; // 交换两个方法

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    self.view.backgroundColor = [UIColor grayColor];
    
    person = [[Person alloc] init];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Button functions

- (IBAction)doGetAllVariable:(id)sender {
    
    unsigned int count = 0;
    
    // 类中所有变量的列表
    Ivar *allVariables = class_copyIvarList([person class], &count);
    for (int i = 0; i < count; i ++) {
        // 遍历变量列表
        Ivar ivar = allVariables[i];
        
        const char *ivarName = ivar_getName(ivar); // 变量名称,实例变量
        const char *ivarType = ivar_getTypeEncoding(ivar); // 变量类型
        NSLog(@"变量名称:%s---变量类型:%s", ivarName, ivarType);
    }
}

- (IBAction)doGetAllMethod:(id)sender {
    
    unsigned int count = 0;
    
    // 获取方法列表,所有在.m文件显式实现的方法都会被找到,包括setter和getter方法
    Method *allMethods = class_copyMethodList([person class], &count);
    for (int i = 0; i < count; i ++) {
        // 遍历方法列表
        Method method = allMethods[i];
        
        // SEL类型,即获取方法选择器@selector()
        SEL sel = method_getName(method);
        // @selector()中方法名称
        const char *methodName = sel_getName(sel);
        NSLog(@"方法名称:%s", methodName);
    }
}

- (IBAction)doChangeVariableValue:(id)sender {
    
    NSLog(@"改变前:%@", person);
    
    unsigned int count = 0;
    
    // 类中所有变量的列表
    Ivar *allVariables = class_copyIvarList([person class], &count);
    Ivar nameIvar = allVariables[0];
    
    // name的hkshen改为wanglanman
    object_setIvar(person, nameIvar, @"wanglanman");
    
    NSLog(@"改变后:%@", person);
    
}

- (IBAction)doAddMethod:(id)sender {
    
    /*
     第一个参数表示Class cls 类型。
     第二个参数表示待调用的方法名称。
     第三个参数(IMP)addingMethod,IMP一个函数指针,这里表示指定具体实现方法addingMethod。
     第四个参数表方法的参数,0代表没有参数。
     */
    // addingMethod在ViewController类中,需要实现class_getMethodImplementation才行
    class_addMethod([person class], @selector(NewMethod), class_getMethodImplementation([ViewController class], @selector(addingMethod)), 0);
    
    [person performSelector:@selector(NewMethod)];
}

- (void)addingMethod {
    
    NSLog(@"已经增加了方法:NewMethod");
}

- (IBAction)doAddVariable:(id)sender {
    
    person.sex = @"男";
    NSLog(@"new variable:%@", person.sex);
}

- (IBAction)doReplaceMethod:(id)sender {
    
    Method method1 = class_getInstanceMethod([person class], @selector(doWork));
    Method method2 = class_getInstanceMethod([person class], @selector(doEat));
    method_exchangeImplementations(method1, method2);
    
    [person doWork];  //查看交换后的结果
}

@end

添加属性需要类别:


//
//  Person+Sex.h
//  RuntimeLearn
//
//  Created by hkshen on 2017/9/13.
//  Copyright © 2017年 hkshen. All rights reserved.
//

#import "Person.h"

@interface Person (Sex)

@property (nonatomic, strong) NSString *sex;

@end

//
//  Person+Sex.m
//  RuntimeLearn
//
//  Created by hkshen on 2017/9/13.
//  Copyright © 2017年 hkshen. All rights reserved.
//

#import "Person+Sex.h"
#import <objc/runtime.h>

const char *keyStr = "Sex"; // 做为key,字符常量,必须是C语言字符串

@implementation Person (Sex)

- (void)setSex:(NSString *)sex {
    
    /*
     第一个参数是需要添加属性的对象。
     第二个参数是属性的key。
     第三个参数是属性的值。
     第四个参数是使用的策略,是一个枚举值,可根据开发需要选择不同的枚举。
     */
    objc_setAssociatedObject(self, keyStr, sex, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)sex {
    
    NSString *sexStr = objc_getAssociatedObject(self, keyStr);
    return sexStr;
}

@end
代码:https://github.com/hkshenSmart/RuntimeLearn.git
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值