Just a back up after learning Runtime.
R:南峰子的技术博客 & http://www.jianshu.com/p/927c8384855a...
Just copy following codes into the .m file , then run it and check results in the console.
Tips:
1,对一个 Class 使用 class 方法获取不到它的 Meta-Class, 只会返回这个 Class.
2,Method, Ivar, IMP 本身为指针, Protocol 为 Obj-c 类, 需要 Protocol *;
3,属性不包含成员变量, 成员变量包含了属性内隐式生成的成员变量('_'+属性名);
4,类方法列表中有一个 C++的析构函数 .cxx_destruct. 在ARC 模式下 dealloc 时, 它被自动添加, 根据继承链调用来释放对象.
5,自定义类继承 NSObject 会出错(Why?), 为自定义类添加方法时, 同时添加 C++IMP, 和@selector(名字:推荐和 IMP 一样, 首字母小写), 和 type(What?"v@:" : v 表示返回值类型为 void, @:表示拥有 self 和 _cmd参数时的 type 第二第三位固定字符);
6,通过取得IMP,我们可以跳过Runtime的消息传递机制,直接执行IMP指向的函数实现,这样省去了Runtime消息传递过程中所做的一系列查找操作,会比直接向对象发送消息高效一些.
7,当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法, 没有则去对象所属类的父类中查找;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找, 没有则去这个类的父类的 meta-class 中查找。
8,xxx_copyxx 到的数组需要 free().
9,只能在 alocate 和 register 新类和新的协议之间进行属性和方法等的更改, 注册后的操作不生效.
*/
#import <UIKit/UIKit.h>
#import "MyClass.h"
#import <objc/runtime.h>
void TestObjClass(id self, SEL _cmd){
printf("Original method is %s\n", __func__);
}
void testObjClass(void){
printf("Method changed to %s\n", __func__);
}
int main(int argc, char * argv[]) {
@autoreleasepool {
MyClass *myClass = [[MyClass alloc] init];
unsigned int outCount = 0;
Class cls = myClass.class;
/*类名*/
NSLog(@"Class name is %s", class_getName(cls));
/*父类名*/
NSLog(@"Super class name is %s", class_getName(class_getSuperclass(cls)));
/*是否是元类*/
NSLog(@"MyClass is %@ mate-class", class_isMetaClass(cls)? @"": @"not");
/*元类*/
Class metaClass = objc_getMetaClass(class_getName(cls));
NSLog(@"%s's meta-class is %s", class_getName(cls), class_getName(metaClass)); //myClass 的元类是 MyClass
NSLog(@"%s's meta-class is %p", class_getName(metaClass), objc_getClass((__bridge void *)metaClass)); //MyClass 的元类是 NSObjcet的元类(0x0).对一个 Class 调用 class 方法只会返回 Class, 不会返回 Meta-Class, 所以使用地址打印.
Class currentClass = [cls class];
for (int i = 0; i < 4; i++) {
NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
currentClass = objc_getClass((__bridge void *)currentClass);
}
/*实例变量大小*/
size_t size = class_getInstanceSize(cls);
NSLog(@"Instance size is %zu", size);
/*成员变量*/
//获得成员变量列表
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = *(ivars + i);
NSLog(@"The %d ivar name is %s", i, ivar_getName(ivar));
} //成员变量包括@interface内: 1,'{}'的成员变量 2,@property内隐式实现的成员变量('_'+属性名)
free(ivars);
Ivar string = class_getInstanceVariable(cls, "_string");
NSLog(@"'_string' ivar name is %s", ivar_getName(string)); //属性 string 的成员变量名
Ivar array = class_getInstanceVariable(cls, "_array");
NSLog(@"'_array' ivar name is %s", ivar_getName(array)); //属性 array 的成员变量名
/*属性操作*/
//获得属性列表
objc_property_t *properties = class_copyPropertyList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = *(properties + i);
NSLog(@"The %d property name is %s", i, property_getName(property));
}
free(properties);
objc_property_t stringP = class_getProperty(cls, "string");
NSLog(@"'string' property name is %s", property_getName(stringP)); //获得属性string
objc_property_t arrayP = class_getProperty(cls, "array");
NSLog(@"'array' property name is %s", property_getName(arrayP));
objc_property_t integer = class_getProperty(cls, "integer");
NSLog(@"'integer' property name is %s", property_getName(integer));
objc_property_t instance1 = class_getProperty(cls, "instance1");
if (instance1) {
NSLog(@"'_instance1' property name is %s", property_getName(instance1));
} //instance1 为 nil, 它为 _instance1 成员变量, 不是属性.
/*方法操作*/
//获得方法列表
Method *methods = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Method method = *(methods + i);
NSLog(@"The %d mehod name is %s", i, method_getName(method));
}
free(methods);
//实例的方法会去类(实例的 Meta-Class)的方法列表内搜索, 类的方法回去元类(类的 Meta-Class)的方法列表内搜索.
Method method1 = class_getInstanceMethod(cls, @selector(method1)); //通过@selector 搜索实例方法列表(ClassMethod),获得实例方法(-,动态)
NSLog(@"The instance method name is %s", method_getName(method1));
Method classMethod = class_getClassMethod(cls, @selector(classMethod1)); //通过@selector 搜索方法列表(Meta-ClassMethod),获得类方法(+,静态)
NSLog(@"The class method name is %s", method_getName(classMethod));
//获得方法实现, 并调用.
IMP imp = class_getMethodImplementation(cls, @selector(method1));
imp();
IMP logStr = class_getMethodImplementation(cls, @selector(logString));
//logStr(); crash! Why?
/*协议*/
Protocol * __unsafe_unretained *protocols = class_copyProtocolList(cls, &outCount);
Protocol *protocol; //protocols是一个协议列表, 在里面按下标获得协议的指针.
for (int i = 0; i < outCount; i++) {
protocol = *(protocols + i);
NSLog(@"Protocol name is %s", protocol_getName(protocol));
}
free(protocols);
NSLog(@"MyClass is %@ conform to protocol %s", class_conformsToProtocol(cls, protocol)? @"" : @"not", protocol_getName(protocol));
/*动态创建类*/
//创建一个 NSError 的子类, 名叫 TestClass, 申请额外空间为0.
Class newClass = objc_allocateClassPair([NSError class], "TestClass", 0);
//为 TestClass 添加一个叫 testObjClass 的方法, 实现为 IMPTestObjClass.
class_addMethod(newClass,
@selector(testObjClass/*只是为 IMP 提供一个搜索的名字, 可随意更改, 建议与 IMP 相同, 首字母小写*/),
(IMP)TestObjClass/*提供了 c++ 的实现, 在调用 selector 时进行调用*/,
"v@:"/*@param types An array of characters that describe the types of the arguments to the method*/);
//增加成员变量
class_addIvar(newClass, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i"); //只有在申请空间和注册类之间调用
//增加属性
objc_property_attribute_t type = {"T","\"NSString\""};
objc_property_attribute_t ownerShip = {"C", ""};
objc_property_attribute_t backingIvar = {"V", "_ivar1"};
objc_property_attribute_t attrs[] = {type, ownerShip, backingIvar};
class_addProperty(newClass, "property2"/*属性名*/, attrs/*属性详情*/, 3/*属性个数*/);
//注册此类
objc_registerClassPair(newClass);
//创建实例对象
id instanceCls = [[newClass alloc] initWithDomain:@"some domain" code:0 userInfo:nil];
//通过@selector 调用c++的实现方法
[instanceCls performSelector:@selector(testObjClass)];
//替换方法的实现
class_replaceMethod(newClass, @selector(testObjClass), (IMP)testObjClass, "v@:");
[instanceCls performSelector:@selector(testObjClass)];
//注销类(不能存在它或它子类的实例)
//objc_disposeClassPair(newClass);
/*动态创建实例*/
/*
id class_createInstance (Class cls, size_t extraBytes) 默认位置动态创建实例,提供额外的参数额外字节数, ARC模式下不可用.
id objc_constructInstance ( Class cls, void *bytes ) 指定位置创建实例
void * objc_destructInstance ( id obj ) 销毁实例, 但不移除引用.
*/
/*实例操作函数*/
/*
id object_copy(id obj, size_t size) 返回指定对象的拷贝, ARC 模式下不可用.
id object_dispose(id obj) 销毁一个指定的对象的内存, ARC 模式下不可用.
NSObject *a = [[NSObject alloc] init]; //父类的实例 a
id newB = object_copy(a, class_getInstanceSize(myClass.class)); //拷贝父类实例, 并申请一个子类空间大小的内存.
object_setClass(newB, myClass.class); //设置父类的 Class 为子类 Class
object_dispose(a); //释放父类原实例
*/
/*类实例中实例变量操作*/
/*
for MRC
Ivar object_setInstanceVariable ( id obj, const char *name, void *value ); //修改类实例的实例变量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue ); //获取类实例的实例变量的值
*/
void * object_getIndexedIvars ( id obj ); //获取给实例变量分配的额外地址指针
/*for ARC, 在 ivar 知道的情况下, 比 object_get/setinstanceVariable 速度快.
id object_getIvar(id obj, Ivar ivar); //获得对象中实例变量的值
id object_setIvar(id obj, Ivar ivar, id valur); //设置对象中实例变量的值
*/
/*针对对象的类进行操作*/
const char * object_getClassName(id obj); //获得对象的类名
Class object_getClass(id obj); //获得对象的类
Class object_setClass(id obj, __unsafe_unretained Class cls); //设置对象的类
/*获得类定义*/
int objc_getClassList ( Class *buffer, int bufferCount ); 获取已注册的类定义的列表, 需要提供 buffer:类列表所需要的空间, bufferCount:类个数.
Class * objc_copyClassList ( unsigned int *outCount ); //创建并返回一个指向所有已注册类的指针列表
//三种方法返回指定类的类定义
Class objc_lookUpClass ( const char *name ); //单次查看, 若类未注册, 返回 nil.
Class objc_getClass ( const char *name ); //会调用类处理回调,并再次确认类是否注册,如果确认未注册,再返回nil.
Class objc_getRequiredClass ( const char *name ); //操作与objc_getClass相同,只不过如果没有找到类,则会杀死进程.
Class objc_getMetaClass ( const char *name ); //返回指定类的元类
/*类型编码(Type Encoding):将每个方法的返回值和参数类型编码为一个字符串, 并关联 selector*/
//@encode()返回这个类型的字符串编码, 任何sizeof()操作参数的类型都可以用于@encode().
//注:Objective-C不支持long double类型。@encode(long double)返回d,与double是一样的.
float a[] = {1.0, 2.0, 3.0};
NSLog(@"array encoding type: %s", @encode(typeof(a)));
//console:array encoding type: [3f]
/*关联对象(AssocatedObject)
类似于成员变量, 但是在 runtime 时添加.
*/
void objc_setAssociatedObject(id object/*大腿*/, const void *key/*标识符, @selector(method)*/, id value/*关联的属性*/, objc_AssociationPolicy policy/*内存策略*/); // 设置 value 为 nil 可覆盖原关联属性.
id objc_getAssociatedObject(id object, const void *key);
void objc_removeAssociatedObjects(id object); // 移除所有关联, 慎用, 使用 set->nil.
/*成员变量操作*/
const char * ivar_getName(Ivar v); //获得名字
const char * ivar_getTypeEncoding(Ivar v); // 获得类型编码字符串
ptrdiff_t ivar_getOffset(Ivar v); // 基祉偏移. ptrdiff_t是C/C++标准库中定义的一个与机器相关的数据类型。ptrdiff_t类型变量通常用来保存两个指针减法操作的结果.
/*属性操作*/
const char * property_getName(objc_property_t property);
const char * property_getAttributes(objc_property_t property);
char * property_copyAttributeValue(objc_property_t property, const char *attributeName); // 获得所描述的属性的值的字符串
objc_property_attribute_t * property_copyAttributeList(objc_property_t property, unsigned int *outCount);
/*方法和消息*/
typedef struct objc_selector *SEL;
/*
1,Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL.
2,父类子类相同的方法, SEL 相同.
3,缺点是导致 OC 不支持泛型.
4,所有的 SEL 组成一个 set 集合, 元素唯一.
5,SEL 就是根据方法名 hash 化一个字符串,而对于字符串的比较仅仅需要比较他们的地址就可以了,可以说速度上无与伦比, 但方法过多会 hash 冲突, 所以应当减少 SEL.
*/
//获取SEL 的方法有三种:
SEL sel0 = sel_registerName(/*const char *str*/"str");
SEL sel1 = @selector(main);
SEL sel2 = NSSelectorFromString(@"main");
/*IMP
函数指针, 指向方法实现的首地址.
*/
id (*IMP)(id, SEL, ...);
/*
参数
id: 指向 self 的指针.实例方法则指向类实例的地址, 类方法则指向元类的地址.
SEL: 方法唯一标识.由方法名和参数hash 化的一个 int 值地址, 用于唯一标识一个方法, 查找 IMP.
...: 方法的实际参数列表
*/
/*Method
用于表示类定义中的方法
*/
typedef struct objc_method *Method;
struct objc_method {
SEL method_name; //方法标识
char *method_types;
IMP method_imp; //方法实现
};
//objc_method结构体相当于在 SEL 和 IMP 之间建立了一个映射.
struct objc_method_description{SEL name; char *types;};
/*方法相关操作函数*/
//id method_invoke ( id receiver, Method m, ... ); 调用指定方法的实现
//void method_invoke_stret ( id receiver, Method m, ... ); 调用返回一个数据结构的方法的实现
//SEL method_getName ( Method m ); 获取方法名
//IMP method_getImplementation ( Method m ); 返回方法的实现
const char * method_getTypeEncoding ( Method m ); // 获取描述方法参数和返回值类型的字符串
char * method_copyReturnType ( Method m ); // 获取方法的返回值类型的字符串
char * method_copyArgumentType ( Method m, unsigned int index ); // 获取方法的指定位置参数的类型字符串
void method_getReturnType ( Method m, char *dst, size_t dst_len ); // 通过引用返回方法的返回值类型字符串
unsigned int method_getNumberOfArguments ( Method m ); // 返回方法的参数的个数
void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len ); // 通过引用返回方法指定位置参数的类型字符串
//struct objc_method_description * method_getDescription ( Method m ); // 返回指定方法的方法描述结构体
//IMP method_setImplementation ( Method m, IMP imp ); // 设置方法的实现, 返回值为之前的实现
void method_exchangeImplementations ( Method m1, Method m2 ); // 交换两个方法的实现
/*
id target;
void(*getMethod)(id,SEL) = (void(*)(id,SEL))[target methodForSelector:@selector(alloc)];
getMethod(target,@selector(alloc)); //获得方法的实现, 并调用
*/
/*方法选择器*/
//const char *sel_getName(SEL sel); 根据 SEL 获得方法的字符串名字
//SEL sel_registerName(const char *str); 在 runtime 注册一个 str 名称的方法, 并映射方法名到 SEL, 然后返回 SEl.
//SEL sel_getUid(const char *str); 在 runtime 注册一个方法, 返回 SEL(和 sel_registerName的区别?).
//BOOL sel_isEqual(SEL lhs, SEL rhs);
/*方法调用流程
[receiver message:arg1 :arg2 :...]->objc_msgSend(receiver, @selector(message), arg1, arg2, ...);
*/
//1,当向一个对象发送消息的时候, 会到这个对象所属的类的方法分发表内查找方法, 如果没有则根据其 super_class 指针去所属的类的父类的分发表中查找. \
2,当向一个类发送消息的时候, 会到这个类所属的类(元类)的方法分发表内查找方法, 如果没有则根据 所属类的 super_class 去其父类的元类中的分发表中查找.
/*消息转发机制
当消息是以[object message]调用时, 如果不能向该对象发送消息则编译时就会报错.
当消息以 performSelector: 调用, 则不会在编译时校验是否接收消息, 而会在运行时执行, 不接收则由 -doesNotRecognizedSelector 扔出异常, 程序崩溃.
可以通过调用 responsesToSelector: 来判断是否响应某个方法.
消息转发机制包含了三个方面:
*/
/*
动态方法解析:
当对象在接收到未知的消息时, 首先会调用 +resolveInstanceMethod:/+resolveClassMethod:, 我们可以在这个方法里面 class_addMethod 来处理.
void functionForMethod1(id self, SEL _cmd) {
NSLog(@"%@, %p", self, _cmd);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selectorString = NSStringFromSelector(sel);
if ([selectorString isEqualToString:@"method1"]) {
class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:"); //在父类添加一个方法的实现
}
return [super resolveInstanceMethod:sel]; //去父类调用新添加的方法
}
*/
/*
备用接受者
当动态方法解析不能处理消息时, 会调用 - (id)forwardingTargetForSelector: 进行接受者转移.详见 Class TestForwarding.
*/
/*
完整消息转发
调用 -forwardInvocation: 来最后一次处理未知消息, 将消息封装为一个 NSInvocation 对象(包括 selector, target, arguments), 它的作用有两个:
1,定位可以响应 invocation 中的未知消息的对象.
2,将 invocation 发送给可以处理的对象, 并将处理结果保存在 invocation 中, 运行时系统将会提取其中的结果发送给消息的原始接收者;
*/
//-forwardInvocation: 中的参数需要重写另一个方法:-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 来获得. 详见 Class TestForwarding.
//NSObject 中的 -forwardInvocation: 方法只是简单的调用了 -doesNotRecognizedSelector.
/*Method Swizzling
用于改变 selector 映射的实际实现的技术. 详情见 Class UIViewController+Swizzling.
*/
/*协议与分类
*/
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分类名
char *class_name OBJC2_UNAVAILABLE; // 分类所属的类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分类所实现的协议列表
};
//objc_category 结构体中的 instance_methods 是 objc_class 中方法列表的一个子集. 而 class_methods 是元类方法列表的一个子集.
//objc_class 中方法列表包含分类中的方法.
typedef struct objc_object Protocol;
/*
Protocol * objc_getProtocol ( const char *name ); // 返回指定的协议
Protocol ** objc_copyProtocolList ( unsigned int *outCount ); // 获取运行时所知道的所有协议的数组
Protocol * objc_allocateProtocol ( const char *name ); // 创建新的协议实例, 若已经存在此协议则返回 nil;
void objc_registerProtocol ( Protocol *proto ); // 在运行时中注册新创建的协议
void protocol_addMethodDescription ( Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod ); // 为协议添加方法
void protocol_addProtocol ( Protocol *proto, Protocol *addition ); // 添加一个已注册的协议到协议中
void protocol_addProperty ( Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty ); // 为协议添加属性
const char * protocol_getName ( Protocol *p ); // 返回协议名
BOOL protocol_isEqual ( Protocol *proto, Protocol *other ); // 测试两个协议是否相等
struct objc_method_description * protocol_copyMethodDescriptionList ( Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount ); // 获取协议中指定条件的方法的方法描述数组
struct objc_method_description protocol_getMethodDescription ( Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod ); // 获取协议中指定方法的方法描述
objc_property_t * protocol_copyPropertyList ( Protocol *proto, unsigned int *outCount ); // 获取协议中的属性列表
objc_property_t protocol_getProperty ( Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty ); // 获取协议的指定属性
Protocol ** protocol_copyProtocolList ( Protocol *proto, unsigned int *outCount ); // 获取协议采用的协议
BOOL protocol_conformsToProtocol ( Protocol *proto, Protocol *other ); // 查看协议是否采用了另一个协议
*/
/*Super
self 是一个隐藏参数, 会在每个方法的第一个参数.
super 是一个编译器标识符, 用于告诉编译器这个方法到父类的方法分发表中查找.
*/
struct objc_super {
//id(struct objc_class *) receiver;
Class super_Class;
};
/*
向 super 发送消息时会调用 objc_msgSendSuper();
1,id objc_msgSendSuper ( struct objc_super *super, SEL op, ... );
2,objc_msgSend(objc_super->receiver, @selector(selector));
3,objc_msgSend(self, @selector(selector));
所以归根结底, 还是向 self 发送消息, 只不过调用的方法是从 super_class 的方法分发表中获得的.
*/
/*库相关操作:
framework, kit
*/
// 获取所有加载的Objective-C框架和动态库的名称
const char ** objc_copyImageNames ( unsigned int *outCount );
// 获取指定类所在动态库
const char * class_getImageName ( Class cls );
// 获取指定库或框架中所有类的类名
const char ** objc_copyClassNamesForImage ( const char *image, unsigned int *outCount );
/*块操作
oc 为了见兼容块操作, 将块绑定到函数指针上进行运行, 删除, 添加等操作.
*/
/*
创建一个指针函数的指针,该函数调用时会调用特定的block
IMP imp_implementationWithBlock ( id block );
// 返回与IMP(使用imp_implementationWithBlock创建的)相关的block
id imp_getBlock ( IMP anImp );
// 解除block与IMP(使用imp_implementationWithBlock创建的)的关联关系,并释放block的拷贝
BOOL imp_removeBlock ( IMP anImp );
*/
/*弱引用操作
不懂什么意思!!!!
*/
// 加载弱引用指针引用的对象并返回
id objc_loadWeak ( id *location );
// 存储__weak变量的新值
id objc_storeWeak ( id *location, id obj );
/*
objc_loadWeak函数:该函数加载一个弱指针引用的对象,并在对其做retain和autoreleasing操作后返回它。这样,对象就可以在调用者使用它时保持足够长的生命周期。该函数典型的用法是在任何有使用__weak变量的表达式中使用。
objc_storeWeak函数:该函数的典型用法是用于__weak变量做为赋值对象时。
*/
return 0;
}
}