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