概述
Objc Runtime使得C具有了面向?qū)ο竽芰γ煜停诔绦蜻\(yùn)行時(shí)創(chuàng)建,檢查请毛,修改類(lèi)志鞍、對(duì)象和它們的方法。Runtime是C和匯編編寫(xiě)的方仿,這里http://www.opensource.apple.com/source/objc4/可以下到蘋(píng)果維護(hù)的開(kāi)源代碼固棚,GNU也有一個(gè)開(kāi)源的runtime版本,他們都努力的保持一致仙蚜。蘋(píng)果官方的Runtime編程指南
Runtime函數(shù)
Runtime系統(tǒng)是由一系列的函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成的公共接口動(dòng)態(tài)共享庫(kù)此洲,在/usr/include/objc目錄下可以看到頭文件,可以用其中一些函數(shù)通過(guò)C語(yǔ)言實(shí)現(xiàn)Objective-C中一樣的功能委粉。蘋(píng)果官方文檔https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html 里有詳細(xì)的Runtime函數(shù)文檔呜师。
Class和Object基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
Class
objc/runtime.h中objc_class結(jié)構(gòu)體的定義如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //isa指針指向Meta Class,因?yàn)镺bjc的類(lèi)的本身也是一個(gè)Object贾节,為了處理這個(gè)關(guān)系汁汗,runtime就創(chuàng)造了Meta Class,當(dāng)給類(lèi)發(fā)送[NSObject alloc]這樣消息時(shí)栗涂,實(shí)際上是把這個(gè)消息發(fā)給了Class Object
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類(lèi)
const char *name OBJC2_UNAVAILABLE; // 類(lèi)名
long version OBJC2_UNAVAILABLE; // 類(lèi)的版本信息知牌,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類(lèi)信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
long instance_size OBJC2_UNAVAILABLE; // 該類(lèi)的實(shí)例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類(lèi)的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存斤程,對(duì)象接到一個(gè)消息會(huì)根據(jù)isa指針查找消息對(duì)象角寸,這時(shí)會(huì)在methodLists中遍歷,如果cache了,常用的方法調(diào)用時(shí)就能夠提高調(diào)用的效率袭厂。
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
objc_ivar_list和objc_method_list的定義
//objc_ivar_list結(jié)構(gòu)體存儲(chǔ)objc_ivar數(shù)組列表
struct objc_ivar_list {
int ivar_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
//objc_method_list結(jié)構(gòu)體存儲(chǔ)著objc_method的數(shù)組列表
struct objc_method_list {
struct objc_method_list *obsolete OBJC2_UNAVAILABLE;
int method_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
struct objc_method method_list[1] OBJC2_UNAVAILABLE;
}
objc_object與id
objc_object是一個(gè)類(lèi)的實(shí)例結(jié)構(gòu)體墨吓,objc/objc.h中objc_object是一個(gè)類(lèi)的實(shí)例結(jié)構(gòu)體定義如下:
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
向object發(fā)送消息時(shí),Runtime庫(kù)會(huì)根據(jù)object的isa指針找到這個(gè)實(shí)例object所屬于的類(lèi)纹磺,然后在類(lèi)的方法列表以及父類(lèi)方法列表尋找對(duì)應(yīng)的方法運(yùn)行帖烘。id是一個(gè)objc_object結(jié)構(gòu)類(lèi)型的指針,這個(gè)類(lèi)型的對(duì)象能夠轉(zhuǎn)換成任何一種對(duì)象橄杨。
objc_cache
objc_class結(jié)構(gòu)體中的cache字段用于緩存調(diào)用過(guò)的method秘症。cache指針指向objc_cache結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體的定義如下
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE; //指定分配緩存bucket的總數(shù)式矫。runtime使用這個(gè)字段確定線性查找數(shù)組的索引位置
unsigned int occupied OBJC2_UNAVAILABLE; //實(shí)際占用緩存bucket總數(shù)
Method buckets[1] OBJC2_UNAVAILABLE; //指向Method數(shù)據(jù)結(jié)構(gòu)指針的數(shù)組乡摹,這個(gè)數(shù)組的總數(shù)不能超過(guò)mask+1,但是指針是可能為空的采转,這就表示緩存bucket沒(méi)有被占用聪廉,數(shù)組會(huì)隨著時(shí)間增長(zhǎng)。
};
Meta Class
meta class是一個(gè)類(lèi)對(duì)象的類(lèi)故慈,當(dāng)向?qū)ο蟀l(fā)消息板熊,runtime會(huì)在這個(gè)對(duì)象所屬類(lèi)方法列表中查找發(fā)送消息對(duì)應(yīng)的方法,但當(dāng)向類(lèi)發(fā)送消息時(shí)察绷,runtime就會(huì)在這個(gè)類(lèi)的meta class方法列表里查找干签。所有的meta class,包括Root class拆撼,Superclass容劳,Subclass的isa都指向Root class的meta class,這樣能夠形成一個(gè)閉環(huán)闸度。
void TestMetaClass(id self, SEL _cmd) {
NSLog(@"This objcet is %p", self);
NSLog(@"Class is %@, super class is %@", [self class], [self superclass]);
Class currentClass = [self class];
//
for (int i = 0; i < 4; i++) {
NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
//通過(guò)objc_getClass獲得對(duì)象isa竭贩,這樣可以回溯到Root class及NSObject的meta class,可以看到最后指針指向的是0x0和NSObject的meta class類(lèi)地址一樣莺禁。
currentClass = objc_getClass((__bridge void *)currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]);
NSLog(@"NSObject's meta class is %p", objc_getClass((__bridge void *)[NSObject class]));
}
@implementation Test
- (void)ex_registerClassPair {
Class newClass = objc_allocateClassPair([NSError class], "TestClass", 0);
class_addMethod(newClass, @selector(testMetaClass), (IMP)TestMetaClass, "v@:");
objc_registerClassPair(newClass);
id instance = [[newClass alloc] initWithDomain:@"some domain" code:0 userInfo:nil];
[instance performSelector:@selector(testMetaClass)];
}
@end
運(yùn)行結(jié)果
2014-10-20 22:57:07.352 mountain[1303:41490] This objcet is 0x7a6e22b0
2014-10-20 22:57:07.353 mountain[1303:41490] Class is TestStringClass, super class is NSError
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 0 times gives 0x7a6e21b0
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 1 times gives 0x0
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 2 times gives 0x0
2014-10-20 22:57:07.353 mountain[1303:41490] Following the isa pointer 3 times gives 0x0
2014-10-20 22:57:07.353 mountain[1303:41490] NSObject's class is 0xe10000
2014-10-20 22:57:07.354 mountain[1303:41490] NSObject's meta class is 0x0
舉個(gè)例子
@interface Sark : NSObject
@end
@implementation Sark
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
NSLog(@"%d %d %d %d", res1, res2, res3, res4);
}
return 0;
}
//輸出
2014-11-05 14:45:08.474 Test[9412:721945] 1 0 0 0
先看看isKindOfClass和isMemberOfClass在Object.mm中的實(shí)現(xiàn)
- (BOOL)isKindOf:aClass
{
Class cls;
for (cls = isa; cls; cls = cls->superclass)
if (cls == (Class)aClass)
return YES;
return NO;
}
- (BOOL)isMemberOf:aClass
{
return isa == (Class)aClass;
}
res1中留量,可以從isKindOfClass看出NSObject class的isa第一次會(huì)指向NSObject的Meta Class,接著Super class時(shí)會(huì)NSObject的Meta Class根據(jù)前面講的閉環(huán)可以知道是會(huì)指到NSObject class睁宰,這樣res1的bool值就是真了肪获。
res2的話因?yàn)槭莍sMemberOf就只有一次寝凌,那么是NSObject的Meta Class和NSObject class不同返回的bool值就是false了柒傻。
res3第一次是Sark Meta Class,第二次super class 后就是NSObject Meta Class了较木,返回也是false
res4是Sark Meta Class红符,所以返回也是false
類(lèi)與對(duì)象操作函數(shù)
runtime有很多的函數(shù)可以操作類(lèi)和對(duì)象。類(lèi)相關(guān)的是class為前綴,對(duì)象相關(guān)操作是objc或object_為前綴预侯。
類(lèi)相關(guān)操作函數(shù)
name
// 獲取類(lèi)的類(lèi)名
const char * class_getName ( Class cls );
super_class和meta-class
// 獲取類(lèi)的父類(lèi)
Class class_getSuperclass ( Class cls );
// 判斷給定的Class是否是一個(gè)meta class
BOOL class_isMetaClass ( Class cls );
instance_size
// 獲取實(shí)例大小
size_t class_getInstanceSize ( Class cls );
成員變量(ivars)及屬性
//成員變量操作函數(shù)
// 獲取類(lèi)中指定名稱(chēng)實(shí)例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
// 獲取類(lèi)成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
// 添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); //這個(gè)只能夠向在runtime時(shí)創(chuàng)建的類(lèi)添加成員變量
// 獲取整個(gè)成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); //必須使用free()來(lái)釋放這個(gè)數(shù)組
//屬性操作函數(shù)
// 獲取類(lèi)中指定名稱(chēng)實(shí)例成員變量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
// 獲取類(lèi)成員變量的信息
Ivar class_getClassVariable ( Class cls, const char *name );
// 添加成員變量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
// 獲取整個(gè)成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
methodLists
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types ); //和成員變量不同的是可以為類(lèi)動(dòng)態(tài)添加方法致开。如果有同名會(huì)返回NO,修改的話需要使用method_setImplementation
// 獲取實(shí)例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 獲取類(lèi)方法
Method class_getClassMethod ( Class cls, SEL name );
// 獲取所有方法的數(shù)組
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
// 替代方法的實(shí)現(xiàn)
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
// 返回方法的具體實(shí)現(xiàn)
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );
// 類(lèi)實(shí)例是否響應(yīng)指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
objc_protocol_list
// 添加協(xié)議
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回類(lèi)是否實(shí)現(xiàn)指定的協(xié)議
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回類(lèi)實(shí)現(xiàn)的協(xié)議列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
version
// 獲取版本號(hào)
int class_getVersion ( Class cls );
// 設(shè)置版本號(hào)
void class_setVersion ( Class cls, int version );
實(shí)例
通過(guò)實(shí)例來(lái)消化下上面的那些函數(shù)
//-----------------------------------------------------------
// MyClass.h
@interface MyClass : NSObject <NSCopying, NSCoding>
@property (nonatomic, strong) NSArray *array;
@property (nonatomic, copy) NSString *string;
- (void)method1;
- (void)method2;
+ (void)classMethod1;
@end
//-----------------------------------------------------------
// MyClass.m
#import "MyClass.h"
@interface MyClass () {
NSInteger _instance1;
NSString * _instance2;
}
@property (nonatomic, assign) NSUInteger integer;
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2;
@end
@implementation MyClass
+ (void)classMethod1 {
}
- (void)method1 {
NSLog(@"call method method1");
}
- (void)method2 {
}
- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2 {
NSLog(@"arg1 : %ld, arg2 : %@", arg1, arg2);
}
@end
//-----------------------------------------------------------
// main.h
#import "MyClass.h"
#import "MySubClass.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyClass *myClass = [[MyClass alloc] init];
unsigned int outCount = 0;
Class cls = myClass.class;
// 類(lèi)名
NSLog(@"class name: %s", class_getName(cls));
NSLog(@"==========================================================");
// 父類(lèi)
NSLog(@"super class name: %s", class_getName(class_getSuperclass(cls)));
NSLog(@"==========================================================");
// 是否是元類(lèi)
NSLog(@"MyClass is %@ a meta-class", (class_isMetaClass(cls) ? @"" : @"not"));
NSLog(@"==========================================================");
Class meta_class = objc_getMetaClass(class_getName(cls));
NSLog(@"%s's meta-class is %s", class_getName(cls), class_getName(meta_class));
NSLog(@"==========================================================");
// 變量實(shí)例大小
NSLog(@"instance size: %zu", class_getInstanceSize(cls));
NSLog(@"==========================================================");
// 成員變量
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
}
free(ivars);
Ivar string = class_getInstanceVariable(cls, "_string");
if (string != NULL) {
NSLog(@"instace variable %s", ivar_getName(string));
}
NSLog(@"==========================================================");
// 屬性操作
objc_property_t * properties = class_copyPropertyList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSLog(@"property's name: %s", property_getName(property));
}
free(properties);
objc_property_t array = class_getProperty(cls, "array");
if (array != NULL) {
NSLog(@"property %s", property_getName(array));
}
NSLog(@"==========================================================");
// 方法操作
Method *methods = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Method method = methods[i];
NSLog(@"method's signature: %s", method_getName(method));
}
free(methods);
Method method1 = class_getInstanceMethod(cls, @selector(method1));
if (method1 != NULL) {
NSLog(@"method %s", method_getName(method1));
}
Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
if (classMethod != NULL) {
NSLog(@"class method : %s", method_getName(classMethod));
}
NSLog(@"MyClass is%@ responsd to selector: method3WithArg1:arg2:", class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @" not");
IMP imp = class_getMethodImplementation(cls, @selector(method1));
imp();
NSLog(@"==========================================================");
// 協(xié)議
Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
Protocol * protocol;
for (int i = 0; i < outCount; i++) {
protocol = protocols[i];
NSLog(@"protocol name: %s", protocol_getName(protocol));
}
NSLog(@"MyClass is%@ responsed to protocol %s", class_conformsToProtocol(cls, protocol) ? @"" : @" not", protocol_getName(protocol));
NSLog(@"==========================================================");
}
return 0;
}
輸出結(jié)果
2014-10-22 19:41:37.452 RuntimeTest[3189:156810] class name: MyClass
2014-10-22 19:41:37.453 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] super class name: NSObject
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] MyClass is not a meta-class
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.454 RuntimeTest[3189:156810] MyClass's meta-class is MyClass
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance size: 48
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _instance1 at index: 0
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _instance2 at index: 1
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _array at index: 2
2014-10-22 19:41:37.455 RuntimeTest[3189:156810] instance variable's name: _string at index: 3
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] instance variable's name: _integer at index: 4
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] instace variable _string
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] property's name: array
2014-10-22 19:41:37.463 RuntimeTest[3189:156810] property's name: string
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] property's name: integer
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] property array
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method1
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method2
2014-10-22 19:41:37.464 RuntimeTest[3189:156810] method's signature: method3WithArg1:arg2:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: integer
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setInteger:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: array
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: string
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setString:
2014-10-22 19:41:37.465 RuntimeTest[3189:156810] method's signature: setArray:
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] method's signature: .cxx_destruct
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] method method1
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] class method : classMethod1
2014-10-22 19:41:37.466 RuntimeTest[3189:156810] MyClass is responsd to selector: method3WithArg1:arg2:
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] call method method1
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] ==========================================================
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] protocol name: NSCopying
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] protocol name: NSCoding
2014-10-22 19:41:37.467 RuntimeTest[3189:156810] MyClass is responsed to protocol NSCoding
2014-10-22 19:41:37.468 RuntimeTest[3189:156810] ======================================
動(dòng)態(tài)創(chuàng)建類(lèi)和對(duì)象
動(dòng)態(tài)創(chuàng)建類(lèi)
// 創(chuàng)建一個(gè)新類(lèi)和元類(lèi)
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes ); //如果創(chuàng)建的是root class萎馅,則superclass為Nil双戳。extraBytes通常為0
// 銷(xiāo)毀一個(gè)類(lèi)及其相關(guān)聯(lián)的類(lèi)
void objc_disposeClassPair ( Class cls ); //在運(yùn)行中還存在或存在子類(lèi)實(shí)例,就不能夠調(diào)用這個(gè)糜芳。
// 在應(yīng)用中注冊(cè)由objc_allocateClassPair創(chuàng)建的類(lèi)
void objc_registerClassPair ( Class cls ); //創(chuàng)建了新類(lèi)后飒货,然后使用class_addMethod,class_addIvar函數(shù)為新類(lèi)添加方法峭竣,實(shí)例變量和屬性后再調(diào)用這個(gè)來(lái)注冊(cè)類(lèi)塘辅,再之后就能夠用了。
使用實(shí)例
Class cls = objc_allocateClassPair(MyClass.class, "MySubClass", 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, "v@:");
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
class_addIvar(cls, "_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(cls, "property2", attrs, 3);
objc_registerClassPair(cls);
id instance = [[cls alloc] init];
[instance performSelector:@selector(submethod1)];
[instance performSelector:@selector(method1)];
輸出
2014-10-23 11:35:31.006 RuntimeTest[3800:66152] run sub method 1
2014-10-23 11:35:31.006 RuntimeTest[3800:66152] run sub method 1
動(dòng)態(tài)創(chuàng)建對(duì)象
// 創(chuàng)建類(lèi)實(shí)例
id class_createInstance ( Class cls, size_t extraBytes ); //會(huì)在heap里給類(lèi)分配內(nèi)存皆撩。這個(gè)方法和+alloc方法類(lèi)似扣墩。
// 在指定位置創(chuàng)建類(lèi)實(shí)例
id objc_constructInstance ( Class cls, void *bytes );
// 銷(xiāo)毀類(lèi)實(shí)例
void * objc_destructInstance ( id obj ); //不會(huì)釋放移除任何相關(guān)引用
測(cè)試下效果
//可以看出class_createInstance和alloc的不同
id theObject = class_createInstance(NSString.class, sizeof(unsigned));
id str1 = [theObject init];
NSLog(@"%@", [str1 class]);
id str2 = [[NSString alloc] initWithString:@"test"];
NSLog(@"%@", [str2 class]);
輸出結(jié)果
2014-10-23 12:46:50.781 RuntimeTest[4039:89088] NSString
2014-10-23 12:46:50.781 RuntimeTest[4039:89088] __NSCFConstantString
實(shí)例操作函數(shù)
這些函數(shù)是針對(duì)創(chuàng)建的實(shí)例對(duì)象的一系列操作函數(shù)。
整個(gè)對(duì)象操作的函數(shù)
// 返回指定對(duì)象的一份拷貝
id object_copy ( id obj, size_t size );
// 釋放指定對(duì)象占用的內(nèi)存
id object_dispose ( id obj );
應(yīng)用場(chǎng)景
//把a(bǔ)轉(zhuǎn)換成占用更多空間的子類(lèi)b
NSObject *a = [[NSObject alloc] init];
id newB = object_copy(a, class_getInstanceSize(MyClass.class));
object_setClass(newB, MyClass.class);
object_dispose(a);
對(duì)象實(shí)例變量進(jìn)行操作的函數(shù)
// 修改類(lèi)實(shí)例的實(shí)例變量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 獲取對(duì)象實(shí)例變量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向給定對(duì)象分配的任何額外字節(jié)的指針
void * object_getIndexedIvars ( id obj );
// 返回對(duì)象中實(shí)例變量的值
id object_getIvar ( id obj, Ivar ivar );
// 設(shè)置對(duì)象中實(shí)例變量的值
void object_setIvar ( id obj, Ivar ivar, id value );
對(duì)對(duì)象類(lèi)操作
// 返回給定對(duì)象的類(lèi)名
const char * object_getClassName ( id obj );
// 返回對(duì)象的類(lèi)
Class object_getClass ( id obj );
// 設(shè)置對(duì)象的類(lèi)
Class object_setClass ( id obj, Class cls );
獲取類(lèi)定義
// 獲取已注冊(cè)的類(lèi)定義的列表
int objc_getClassList ( Class *buffer, int bufferCount );
// 創(chuàng)建并返回一個(gè)指向所有已注冊(cè)類(lèi)的指針列表
Class * objc_copyClassList ( unsigned int *outCount );
// 返回指定類(lèi)的類(lèi)定義
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );
// 返回指定類(lèi)的元類(lèi)
Class objc_getMetaClass ( const char *name );
演示如何使用
int numClasses;
Class * classes = NULL;
numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0) {
classes = malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
NSLog(@"number of classes: %d", numClasses);
for (int i = 0; i < numClasses; i++) {
Class cls = classes[i];
NSLog(@"class name: %s", class_getName(cls));
}
free(classes);
}
結(jié)果如下:
2014-10-23 16:20:52.589 RuntimeTest[8437:188589] number of classes: 1282
2014-10-23 16:20:52.589 RuntimeTest[8437:188589] class name: DDTokenRegexp
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: _NSMostCommonKoreanCharsKeySet
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: OS_xpc_dictionary
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: NSFileCoordinator
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: NSAssertionHandler
2014-10-23 16:20:52.590 RuntimeTest[8437:188589] class name: PFUbiquityTransactionLogMigrator
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: NSNotification
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: NSKeyValueNilSetEnumerator
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: OS_tcp_connection_tls_session
2014-10-23 16:20:52.591 RuntimeTest[8437:188589] class name: _PFRoutines
......還有大量輸出
成員變量與屬性
基礎(chǔ)數(shù)據(jù)類(lèi)型
Ivar
實(shí)例變量類(lèi)型扛吞,指向objc_ivar結(jié)構(gòu)體的指針呻惕,ivar指針地址是根據(jù)class結(jié)構(gòu)體的地址加上基地址偏移字節(jié)得到的。
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE; // 變量名
char *ivar_type OBJC2_UNAVAILABLE; // 變量類(lèi)型
int ivar_offset OBJC2_UNAVAILABLE; // 基地址偏移字節(jié)
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
objc_property_t
屬性類(lèi)型喻粹,指向objc_property結(jié)構(gòu)體
typedef struct objc_property *objc_property_t;
通過(guò)class_copyPropertyList和protocol_copyPropertyList方法獲取類(lèi)和協(xié)議的屬性
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
示例
@interface Lender : NSObject {
float alone;
}
@property float alone;
@end
//獲取屬性列表
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
//查找屬性名稱(chēng)
const char *property_getName(objc_property_t property)
//通過(guò)給出的名稱(chēng)來(lái)在類(lèi)和協(xié)議中獲取屬性的引用
objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
//發(fā)掘?qū)傩悦Q(chēng)和@encode類(lèi)型字符串
const char *property_getAttributes(objc_property_t property)
//從一個(gè)類(lèi)中獲取它的屬性
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
objc_property_attribute_t
也是結(jié)構(gòu)體蟆融,定義屬性的attribute
typedef struct {
const char *name; // 特性名
const char *value; // 特性值
} objc_property_attribute_t;
示例
下面代碼會(huì)編譯出錯(cuò),Runtime Crash還是正常輸出
@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Sark
- (void)speak
{
NSLog(@"my name is %@", self.name);
}
@end
@interface Test : NSObject
@end
@implementation Test
- (instancetype)init
{
self = [super init];
if (self) {
id cls = [Sark class];
void *obj = &cls;
[(__bridge id)obj speak];
}
return self;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[[Test alloc] init];
}
return 0;
}
//結(jié)果正常輸出
2014-11-07 14:08:25.698 Test[1097:57255] my name is
obj為指向Sark Class的指針守呜,相當(dāng)于Sark的實(shí)例對(duì)象但是還是不一樣型酥,根據(jù)objc_msgSend流程,obj指針能夠在方法列表中找到speak方法查乒,所以運(yùn)行正常弥喉。
為了得到self.name能夠輸出的原因,可以加入調(diào)試代碼
- (void)speak
{
unsigned int numberOfIvars = 0;
Ivar *ivars = class_copyIvarList([self class], &numberOfIvars);
for(const Ivar *p = ivars; p < ivars+numberOfIvars; p++) {
Ivar const ivar = *p;
ptrdiff_t offset = ivar_getOffset(ivar);
const char *name = ivar_getName(ivar);
NSLog(@"Sark ivar name = %s, offset = %td", name, offset);
}
NSLog(@"my name is %p", &_name);
NSLog(@"my name is %@", *(&_name));
}
@implementation Test
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"Test instance = %@", self);
void *self2 = (__bridge void *)self;
NSLog(@"Test instance pointer = %p", &self2);
id cls = [Sark class];
NSLog(@"Class instance address = %p", cls);
void *obj = &cls;
NSLog(@"Void *obj = %@", obj);
[(__bridge id)obj speak];
}
return self;
}
@end
//輸出
2014-11-11 00:56:02.464 Test[10475:1071029] Test instance = 2014-11-11 00:56:02.464 Test[10475:1071029] Test instance pointer = 0x7fff5fbff7c8
2014-11-11 00:56:02.465 Test[10475:1071029] Class instance address = 0x1000023c8
2014-11-11 00:56:02.465 Test[10475:1071029] Void *obj = 2014-11-11 00:56:02.465 Test[10475:1071029] Sark ivar name = _name, offset = 8
2014-11-11 00:56:02.465 Test[10475:1071029] my name is 0x7fff5fbff7c8
2014-11-11 00:56:02.465 Test[10475:1071029] my name is
Sark中Propertyname會(huì)被轉(zhuǎn)換成ivar到類(lèi)的結(jié)構(gòu)里玛迄,runtime會(huì)計(jì)算ivar的地址偏移來(lái)找ivar的最終地址由境,根據(jù)輸出可以看出Sark class的指針地址加上ivar的偏移量正好跟Test對(duì)象指針地址。
關(guān)聯(lián)對(duì)象
關(guān)聯(lián)對(duì)象是在運(yùn)行時(shí)添加的類(lèi)似成員蓖议。
//將一個(gè)對(duì)象連接到其它對(duì)象
static char myKey;
objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN);
//獲取一個(gè)新的關(guān)聯(lián)的對(duì)象
id anObject = objc_getAssociatedObject(self, &myKey);
//使用objc_removeAssociatedObjects函數(shù)移除一個(gè)關(guān)聯(lián)對(duì)象
實(shí)例演示關(guān)聯(lián)對(duì)象使用
//動(dòng)態(tài)的將一個(gè)Tap手勢(shì)操作連接到任何UIView中虏杰。
- (void)setTapActionWithBlock:(void (^)(void))block
{
UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerTapGestureKey);
if (!gesture)
{
gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
[self addGestureRecognizer:gesture];
//將創(chuàng)建的手勢(shì)對(duì)象和block作為關(guān)聯(lián)對(duì)象
objc_setAssociatedObject(self, &kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
}
objc_setAssociatedObject(self, &kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}
//手勢(shì)識(shí)別對(duì)象的target和action
- (void)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateRecognized)
{
void(^action)(void) = objc_getAssociatedObject(self, &kDTActionHandlerTapBlockKey);
if (action)
{
action();
}
}
}
成員變量和屬性的操作方法
成員變量
// 獲取成員變量名
const char * ivar_getName ( Ivar v );
// 獲取成員變量類(lèi)型編碼
const char * ivar_getTypeEncoding ( Ivar v );
// 獲取成員變量的偏移量
ptrdiff_t ivar_getOffset ( Ivar v );
Associated Objects
// 設(shè)置關(guān)聯(lián)對(duì)象
void objc_setAssociatedObject ( id object, const void *key, id value, objc_AssociationPolicy policy );
// 獲取關(guān)聯(lián)對(duì)象
id objc_getAssociatedObject ( id object, const void *key );
// 移除關(guān)聯(lián)對(duì)象
void objc_removeAssociatedObjects ( id object );
//上面方法以鍵值對(duì)的形式動(dòng)態(tài)的向?qū)ο筇砑樱@取或者刪除關(guān)聯(lián)值勒虾。其中關(guān)聯(lián)政策是一組枚舉常量纺阔。這些常量對(duì)應(yīng)著引用關(guān)聯(lián)值機(jī)制,也就是Objc內(nèi)存管理的引用計(jì)數(shù)機(jī)制修然。
enum {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
屬性
// 獲取屬性名
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 );
實(shí)例
兩個(gè)接口同樣數(shù)據(jù)不同的字段名處理
@interface MyObject: NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSString * status;
@end
//返回字典數(shù)據(jù)有不同的字段名笛钝,一般是寫(xiě)兩個(gè)方法质况,但是如果靈活用runtime只用寫(xiě)一個(gè)方法
@{@"name1": "張三", @"status1": @"start"}
@{@"name2": "張三", @"status2": @"end"}
//定義一個(gè)映射字典(全局)
static NSMutableDictionary *map = nil;
@implementation MyObject
+ (void)load
{
map = [NSMutableDictionary dictionary];
map[@"name1"] = @"name";
map[@"status1"] = @"status";
map[@"name2"] = @"name";
map[@"status2"] = @"status";
}
@end
//不同字段映射到MyObject相同屬性上
- (void)setDataWithDic:(NSDictionary *)dic
{
[dic enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
NSString *propertyKey = [self propertyForKey:key];
if (propertyKey)
{
objc_property_t property = class_getProperty([self class], [propertyKey UTF8String]);
// TODO: 針對(duì)特殊數(shù)據(jù)類(lèi)型做處理
NSString *attributeString = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
...
[self setValue:obj forKey:propertyKey];
}
}];
}
Method和消息
Method和消息的基礎(chǔ)數(shù)據(jù)類(lèi)型
SEL
選擇器表示一個(gè)方法的selector的指針,可以理解為Method中的ID類(lèi)型
typedef struct objc_selector *SEL;
//objc_selector編譯時(shí)會(huì)根據(jù)每個(gè)方法名字參數(shù)序列生成唯一標(biāo)識(shí)
SEL sel1 = @selector(method1);
NSLog(@"sel : %p", sel1);
輸出
2014-10-30 18:40:07.518 RuntimeTest[52734:466626] sel : 0x100002d72
獲取SEL的三個(gè)方法:
- sel_registerName函數(shù)
- Objective-C編譯器提供的@selector()
- NSSelectorFromString()方法
IMP
是函數(shù)指針玻靡,指向方法的首地址结榄,通過(guò)SEL快速得到對(duì)應(yīng)IMP,這時(shí)可以跳過(guò)Runtime消息傳遞機(jī)制直接執(zhí)行函數(shù),比直接向?qū)ο蟀l(fā)消息高效。定義如下
id (*IMP)(id, SEL, ...)
Method
用于表示類(lèi)定義中的方法
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE; //是個(gè)char指針习绢,存儲(chǔ)著方法的參數(shù)類(lèi)型和返回值類(lèi)型
IMP method_imp OBJC2_UNAVAILABLE; // 方法實(shí)現(xiàn),函數(shù)指針
}
Method相關(guān)操作函數(shù)
Method
// 調(diào)用指定方法的實(shí)現(xiàn)依溯,返回的是方法實(shí)現(xiàn)時(shí)的返回,參數(shù)receiver不能為空瘟则,這個(gè)比method_getImplementation和method_getName快
id method_invoke ( id receiver, Method m, ... );
// 調(diào)用返回一個(gè)數(shù)據(jù)結(jié)構(gòu)的方法的實(shí)現(xiàn)
void method_invoke_stret ( id receiver, Method m, ... );
// 獲取方法名黎炉,希望獲得方法明的C字符串,使用sel_getName(method_getName(method))
SEL method_getName ( Method m );
// 返回方法的實(shí)現(xiàn)
IMP method_getImplementation ( Method m );
// 獲取描述方法參數(shù)和返回值類(lèi)型的字符串
const char * method_getTypeEncoding ( Method m );
// 獲取方法的返回值類(lèi)型的字符串
char * method_copyReturnType ( Method m );
// 獲取方法的指定位置參數(shù)的類(lèi)型字符串
char * method_copyArgumentType ( Method m, unsigned int index );
// 通過(guò)引用返回方法的返回值類(lèi)型字符串
void method_getReturnType ( Method m, char *dst, size_t dst_len );
// 返回方法的參數(shù)的個(gè)數(shù)
unsigned int method_getNumberOfArguments ( Method m );
// 通過(guò)引用返回方法指定位置參數(shù)的類(lèi)型字符串
void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len );
// 返回指定方法的方法描述結(jié)構(gòu)體
struct objc_method_description * method_getDescription ( Method m );
// 設(shè)置方法的實(shí)現(xiàn)
IMP method_setImplementation ( Method m, IMP imp );
// 交換兩個(gè)方法的實(shí)現(xiàn)
void method_exchangeImplementations ( Method m1, Method m2 );
Method的SEL
// 返回給定選擇器指定的方法的名稱(chēng)
const char * sel_getName ( SEL sel );
// 在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法醋拧,將方法名映射到一個(gè)選擇器慷嗜,并返回這個(gè)選擇器
SEL sel_registerName ( const char *str );
// 在Objective-C Runtime系統(tǒng)中注冊(cè)一個(gè)方法
SEL sel_getUid ( const char *str );
// 比較兩個(gè)選擇器
BOOL sel_isEqual ( SEL lhs, SEL rhs );
Method調(diào)用流程
消息函數(shù),Objc中發(fā)送消息是用中括號(hào)把接收者和消息括起來(lái)丹壕,只到運(yùn)行時(shí)才會(huì)把消息和方法實(shí)現(xiàn)綁定庆械。
//這個(gè)函數(shù)將消息接收者和方法名作為基礎(chǔ)參數(shù)。消息發(fā)送給一個(gè)對(duì)象時(shí)菌赖,objc_msgSend通過(guò)對(duì)象的isa指針獲得類(lèi)的結(jié)構(gòu)體缭乘,先在Cache里找,找到就執(zhí)行琉用,沒(méi)找到就在分發(fā)列表里查找方法的selector堕绩,沒(méi)找到就通過(guò)objc_msgSend結(jié)構(gòu)體中指向父類(lèi)的指針找到父類(lèi),然后在父類(lèi)分發(fā)列表找邑时,直到root class(NSObject)奴紧。
objc_msgSend(receiver, selector, arg1, arg2, ...)
編譯器會(huì)根據(jù)情況在objc_msgSend,objc_msgSend_stret晶丘,objc_msgSendSuper黍氮,或objc_msgSendSuper_stret四個(gè)方法中選一個(gè)調(diào)用。如果是傳遞給超類(lèi)就會(huì)調(diào)用帶super的函數(shù)浅浮,如果返回是數(shù)據(jù)結(jié)構(gòu)而不是一個(gè)值就會(huì)調(diào)用帶stret的函數(shù)沫浆。在i386平臺(tái)返回類(lèi)型為浮點(diǎn)消息會(huì)調(diào)用objc_msgSend_fpret函數(shù)。
舉個(gè)例子滚秩,NSStringFromClass([self class])和NSStringFromClass([super class])輸出都是self的類(lèi)名专执。原因如下
調(diào)用[self class]的時(shí)候先調(diào)用objc_msgSend,發(fā)現(xiàn)self沒(méi)有class這個(gè)方法叔遂,然后用objc_msgSendSuper就去父類(lèi)找他炊,還是沒(méi)有,繼續(xù)用objc_msgSendSuper到NSObject里找已艰,結(jié)果找到了痊末,查找NSObject中class方法的runtime源碼會(huì)發(fā)現(xiàn)它會(huì)返回self,所以[self class]會(huì)返回self本身哩掺。同理[super class]相對(duì)前者就是少了objc_msgSend這一步凿叠,最后也會(huì)找到NSObject根類(lèi)里的class方法,自然結(jié)果也是返回了self嚼吞。
Method中的接收消息對(duì)象參數(shù)和方法選擇器參數(shù)
在Method中使用self關(guān)鍵字來(lái)引用實(shí)例本身盒件,self的內(nèi)容即接收消息的對(duì)象是在Method運(yùn)行時(shí)被傳入的同時(shí)還有方法選擇器。
獲取Method地址
使用NSObject提供的methodForSelector:方法可以獲得Method的指針舱禽,通過(guò)指針調(diào)用實(shí)現(xiàn)代碼炒刁。
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
setter(targetList[i], @selector(setFilled:), YES);
Method轉(zhuǎn)發(fā)
如果使用[object message]調(diào)用方法,object無(wú)法響應(yīng)message時(shí)就會(huì)報(bào)錯(cuò)誊稚。用performSelector...調(diào)用就要等到運(yùn)行時(shí)才確定是否能接受翔始,不能才崩潰。
//先調(diào)用respondsToSelector:來(lái)判斷一下
if ([self respondsToSelector:@selector(method)]) {
[self performSelector:@selector(method)];
}
Method轉(zhuǎn)發(fā)機(jī)制分為三步:
動(dòng)態(tài)方法解析
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];
}
可以動(dòng)態(tài)的提供一個(gè)方法的實(shí)現(xiàn)里伯。例如可以用@dynamic關(guān)鍵字在類(lèi)的實(shí)現(xiàn)文件中寫(xiě)個(gè)屬性
//這個(gè)表明會(huì)為這個(gè)屬性動(dòng)態(tài)提供set get方法城瞎,就是編譯器是不會(huì)默認(rèn)生成setPropertyName:和propertyName方法,需要?jiǎng)討B(tài)提供疾瓮〔倍疲可以通過(guò)重載resolveInstanceMethod:和resolveClassMethod:方法分別添加實(shí)例方法和類(lèi)方法實(shí)現(xiàn)。最后用class_addMethod完成添加特定方法實(shí)現(xiàn)的操作
@dynamic propertyName;
//
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
//v@:表示返回值和參數(shù)狼电,可以在蘋(píng)果官網(wǎng)查看Type Encoding相關(guān)文檔 https://developer.apple.com/library/mac/DOCUMENTATION/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
重定向接收者
如果無(wú)法處理消息會(huì)繼續(xù)調(diào)用下面的方法蜒灰,同時(shí)在這里Runtime系統(tǒng)實(shí)際上是給了一個(gè)替換消息接收者的機(jī)會(huì),但是替換的對(duì)象千萬(wàn)不要是self肩碟,那樣會(huì)進(jìn)入死循環(huán)卷员。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if(aSelector == @selector(mysteriousMethod:)){
return alternateObject;
}
return [super forwardingTargetForSelector:aSelector];
}
使用這個(gè)方法通常在對(duì)象內(nèi)部,如下
@interface SUTRuntimeMethodHelper : NSObject
- (void)method2;
@end
@implementation SUTRuntimeMethodHelper
- (void)method2 {
NSLog(@"%@, %p", self, _cmd);
}
@end
#pragma mark -
@interface SUTRuntimeMethod () {
SUTRuntimeMethodHelper *_helper;
}
@end
@implementation SUTRuntimeMethod
+ (instancetype)object {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self != nil) {
_helper = [[SUTRuntimeMethodHelper alloc] init];
}
return self;
}
- (void)test {
[self performSelector:@selector(method2)];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"forwardingTargetForSelector");
NSString *selectorString = NSStringFromSelector(aSelector);
// 將消息轉(zhuǎn)發(fā)給_helper來(lái)處理
if ([selectorString isEqualToString:@"method2"]) {
return _helper;
}
return [super forwardingTargetForSelector:aSelector];
}
@end
最后進(jìn)行轉(zhuǎn)發(fā)
如果以上兩種都沒(méi)法處理未知消息就需要完整消息轉(zhuǎn)發(fā)了腾务。調(diào)用如下方法
//這一步是最后機(jī)會(huì)將消息轉(zhuǎn)發(fā)給其它對(duì)象毕骡,對(duì)象會(huì)將未處理的消息相關(guān)的selector,target和參數(shù)都封裝在anInvocation中岩瘦。forwardInvocation:像未知消息分發(fā)中心未巫,將未知消息轉(zhuǎn)發(fā)給其它對(duì)象。注意的是forwardInvocation:方法只有在消息接收對(duì)象無(wú)法正常響應(yīng)消息時(shí)才被調(diào)用启昧。
- (void)forwardInvocation:(NSInvocation *)anInvocation
//必須重寫(xiě)這個(gè)方法叙凡,消息轉(zhuǎn)發(fā)使用這個(gè)方法獲得的信息創(chuàng)建NSInvocation對(duì)象。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
范例
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
if ([SUTRuntimeMethodHelper instancesRespondToSelector:aSelector]) {
signature = [SUTRuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([SUTRuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:_helper];
}
}
轉(zhuǎn)發(fā)和多繼承
轉(zhuǎn)發(fā)和繼承相似密末,一個(gè)Object把消息轉(zhuǎn)發(fā)出去就好像它繼承了另一個(gè)Object的方法一樣握爷。
Message消息的參考文章
- Message forwarding https://mikeash.com/pyblog/friday-qa-2009-03-27-objective-c-message-forwarding.html
- Objective-c messaging https://www.mikeash.com/pyblog/friday-qa-2009-03-20-objective-c-messaging.html
- The faster objc_msgSend http://www.mulle-kybernetik.com/artikel/Optimization/opti-9.html
Method Swizzling
是改變一個(gè)selector實(shí)際實(shí)現(xiàn)的技術(shù)跛璧,可以在運(yùn)行時(shí)修改selector對(duì)應(yīng)的函數(shù)來(lái)修改Method的實(shí)現(xiàn)。前面的消息轉(zhuǎn)發(fā)很強(qiáng)大新啼,但是需要能夠修改對(duì)應(yīng)類(lèi)的源碼追城,但是對(duì)于有些類(lèi)無(wú)法修改其源碼時(shí)又要更改其方法實(shí)現(xiàn)時(shí)可以使用Method Swizzling,通過(guò)重新映射方法來(lái)達(dá)到目的燥撞,但是跟消息轉(zhuǎn)發(fā)比起來(lái)調(diào)試會(huì)困難座柱。
使用method swizzling需要注意的問(wèn)題
- Swizzling應(yīng)該總在+load中執(zhí)行:Objective-C在運(yùn)行時(shí)會(huì)自動(dòng)調(diào)用類(lèi)的兩個(gè)方法+load和+initialize。+load會(huì)在類(lèi)初始加載時(shí)調(diào)用物舒,和+initialize比較+load能保證在類(lèi)的初始化過(guò)程中被加載
- Swizzling應(yīng)該總是在dispatch_once中執(zhí)行:swizzling會(huì)改變?nèi)譅顟B(tài)色洞,所以在運(yùn)行時(shí)采取一些預(yù)防措施,使用dispatch_once就能夠確保代碼不管有多少線程都只被執(zhí)行一次冠胯。這將成為method swizzling的最佳實(shí)踐火诸。
- Selector,Method和Implementation:這幾個(gè)之間關(guān)系可以這樣理解荠察,一個(gè)類(lèi)維護(hù)一個(gè)運(yùn)行時(shí)可接收的消息分發(fā)表惭蹂,分發(fā)表中每個(gè)入口是一個(gè)Method,其中key是一個(gè)特定的名稱(chēng)割粮,及SEL盾碗,與其對(duì)應(yīng)的實(shí)現(xiàn)是IMP即指向底層C函數(shù)的指針。
舉例說(shuō)明如何使用Method Swizzling對(duì)一個(gè)類(lèi)中注入一些我們的新的操作舀瓢。
#import <objc/runtime.h>
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
//通過(guò)method swizzling修改了UIViewController的@selector(viewWillAppear:)的指針使其指向了自定義的xxx_viewWillAppear
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
//如果類(lèi)中不存在要替換的方法廷雅,就先用class_addMethod和class_replaceMethod函數(shù)添加和替換兩個(gè)方法實(shí)現(xiàn)。但如果已經(jīng)有了要替換的方法京髓,就調(diào)用method_exchangeImplementations函數(shù)交換兩個(gè)方法的Implementation航缀。
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
method_exchangeImplementations做的事情和如下代碼是一樣的
IMP imp1 = method_getImplementation(m1);
IMP imp2 = method_getImplementation(m2);
method_setImplementation(m1, imp2);
method_setImplementation(m2, imp1);
另一種Method Swizzling的實(shí)現(xiàn)
- (void)replacementReceiveMessage:(const struct BInstantMessage *)arg1 {
NSLog(@"arg1 is %@", arg1);
[self replacementReceiveMessage:arg1];
}
+ (void)load {
SEL originalSelector = @selector(ReceiveMessage:);
SEL overrideSelector = @selector(replacementReceiveMessage:);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method overrideMethod = class_getInstanceMethod(self, overrideSelector);
if (class_addMethod(self, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod))) {
class_replaceMethod(self, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, overrideMethod);
}
}
這里有幾個(gè)關(guān)于Method Swizzling的資源可以參考
- How do I implement method swizzling? http://stackoverflow.com/questions/5371601/how-do-i-implement-method-swizzling
- Method Swizzling http://nshipster.com/method-swizzling/
- What are the Dangers of Method Swizzling in Objective C? http://stackoverflow.com/questions/5339276/what-are-the-dangers-of-method-swizzling-in-objective-c
- JRSwizzle https://github.com/rentzsch/jrswizzle
Protocol和Category
基礎(chǔ)數(shù)據(jù)類(lèi)型
Category
指向分類(lèi)的結(jié)構(gòu)體的指針
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分類(lèi)名
char *class_name OBJC2_UNAVAILABLE; // 分類(lèi)所屬的類(lèi)名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 實(shí)例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 類(lèi)方法列表,Meta Class方法列表的子集
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分類(lèi)所實(shí)現(xiàn)的協(xié)議列表
}
Category里面的方法加載過(guò)程堰怨,objc源碼中找到objc-os.mm芥玉,函數(shù)_objc_init就是runtime的加載入口由libSystem調(diào)用,開(kāi)始初始化备图,之后objc-runtime-new.mm里的map_images會(huì)加載map到內(nèi)存灿巧,_read_images開(kāi)始初始化這個(gè)map,這時(shí)會(huì)load所有Class揽涮,Protocol和Category抠藕,NSObject的+load方法就是這個(gè)時(shí)候調(diào)用的。下面是加載代碼
// Discover categories.
for (EACH_HEADER) {
category_t **catlist = _getObjc2CategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
BOOL classExists = NO;
if (cat->instanceMethods || cat->protocols || cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods || cat->protocols /* || cat->classProperties */)
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
//調(diào)用remethodizeClass方法蒋困,在其實(shí)現(xiàn)里調(diào)用attachCategoryMethods
static void
attachCategoryMethods(Class cls, category_list *cats, bool flushCaches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
method_list_t **mlists = (method_list_t **)
_malloc_internal(cats->count * sizeof(*mlists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int i = cats->count;
BOOL fromBundle = NO;
while (i--) {
method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= cats->list[i].fromBundle;
}
}
attachMethodLists(cls, mlists, mcount, NO, fromBundle, flushCaches);
_free_internal(mlists);
}
示例盾似,下面的代碼會(huì)編譯錯(cuò)誤,Runtime Crash還是會(huì)正常輸出
@interface NSObject (Sark)
+ (void)foo;
@end
@implementation NSObject (Sark)
- (void)foo
{
NSLog(@"IMP: -[NSObject(Sark) foo]");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[NSObject foo];
[[NSObject new] foo];
}
return 0;
}
//結(jié)果雪标,正常輸出結(jié)果如下
2014-11-06 13:11:46.694 Test[14872:1110786] IMP: -[NSObject(Sark) foo]
2014-11-06 13:11:46.695 Test[14872:1110786] IMP: -[NSObject(Sark) foo]
objc runtime加載后NSObject的Sark Category被加載零院,頭文件+(void)foo沒(méi)有IMP溉跃,只會(huì)出現(xiàn)一個(gè)warning。被加到Class的Method list里的方法只有-(void)foo告抄,Meta Class的方法列表里沒(méi)有撰茎。
執(zhí)行[NSObject foo]時(shí),會(huì)在Meta Class的Method list里找玄妈,找不著就繼續(xù)往super class里找,NSObject Meta Clas的super class是NSObject本身髓梅,這時(shí)在NSObject的Method list里就有foo這個(gè)方法了拟蜻,能夠正常輸出。
執(zhí)行[[NSObject new] foo]就簡(jiǎn)單的多了枯饿,[NSObject new]生成一個(gè)實(shí)例酝锅,實(shí)例的Method list是有foo方法的,于是正常輸出奢方。
Protocol
Protocol其實(shí)就是一個(gè)對(duì)象結(jié)構(gòu)體
typedef struct objc_object Protocol;
操作函數(shù)
Category操作函數(shù)信息都包含在objc_class中搔扁,我們可以通過(guò)objc_class的操作函數(shù)來(lái)獲取分類(lèi)的操作函數(shù)信息。
@interface RuntimeCategoryClass : NSObject
- (void)method1;
@end
@interface RuntimeCategoryClass (Category)
- (void)method2;
@end
@implementation RuntimeCategoryClass
- (void)method1 {
}
@end
@implementation RuntimeCategoryClass (Category)
- (void)method2 {
}
@end
#pragma mark -
NSLog(@"測(cè)試objc_class中的方法列表是否包含分類(lèi)中的方法");
unsigned int outCount = 0;
Method *methodList = class_copyMethodList(RuntimeCategoryClass.class, &outCount);
for (int i = 0; i < outCount; i++) {
Method method = methodList[i];
const char *name = sel_getName(method_getName(method));
NSLog(@"RuntimeCategoryClass's method: %s", name);
if (strcmp(name, sel_getName(@selector(method2)))) {
NSLog(@"分類(lèi)方法method2在objc_class的方法列表中");
}
}
//輸出
2014-11-08 10:36:39.213 [561:151847] 測(cè)試objc_class中的方法列表是否包含分類(lèi)中的方法
2014-11-08 10:36:39.215 [561:151847] RuntimeCategoryClass's method: method2
2014-11-08 10:36:39.215 [561:151847] RuntimeCategoryClass's method: method1
2014-11-08 10:36:39.215 [561:151847] 分類(lèi)方法method2在objc_class的方法列表中
Runtime提供了Protocol的一系列函數(shù)操作蟋字,函數(shù)包括
// 返回指定的協(xié)議
Protocol * objc_getProtocol ( const char *name );
// 獲取運(yùn)行時(shí)所知道的所有協(xié)議的數(shù)組
Protocol ** objc_copyProtocolList ( unsigned int *outCount );
// 創(chuàng)建新的協(xié)議實(shí)例
Protocol * objc_allocateProtocol ( const char *name );
// 在運(yùn)行時(shí)中注冊(cè)新創(chuàng)建的協(xié)議
void objc_registerProtocol ( Protocol *proto ); //創(chuàng)建一個(gè)新協(xié)議后必須使用這個(gè)進(jìn)行注冊(cè)這個(gè)新協(xié)議稿蹲,但是注冊(cè)后不能夠再修改和添加新方法。
// 為協(xié)議添加方法
void protocol_addMethodDescription ( Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod );
// 添加一個(gè)已注冊(cè)的協(xié)議到協(xié)議中
void protocol_addProtocol ( Protocol *proto, Protocol *addition );
// 為協(xié)議添加屬性
void protocol_addProperty ( Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty );
// 返回協(xié)議名
const char * protocol_getName ( Protocol *p );
// 測(cè)試兩個(gè)協(xié)議是否相等
BOOL protocol_isEqual ( Protocol *proto, Protocol *other );
// 獲取協(xié)議中指定條件的方法的方法描述數(shù)組
struct objc_method_description * protocol_copyMethodDescriptionList ( Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount );
// 獲取協(xié)議中指定方法的方法描述
struct objc_method_description protocol_getMethodDescription ( Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod );
// 獲取協(xié)議中的屬性列表
objc_property_t * protocol_copyPropertyList ( Protocol *proto, unsigned int *outCount );
// 獲取協(xié)議的指定屬性
objc_property_t protocol_getProperty ( Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty );
// 獲取協(xié)議采用的協(xié)議
Protocol ** protocol_copyProtocolList ( Protocol *proto, unsigned int *outCount );
// 查看協(xié)議是否采用了另一個(gè)協(xié)議
BOOL protocol_conformsToProtocol ( Protocol *proto, Protocol *other );
Block
runtime中一些支持block操作的函數(shù)
// 創(chuàng)建一個(gè)指針函數(shù)的指針鹊奖,該函數(shù)調(diào)用時(shí)會(huì)調(diào)用特定的block
IMP imp_implementationWithBlock ( id block );
// 返回與IMP(使用imp_implementationWithBlock創(chuàng)建的)相關(guān)的block
id imp_getBlock ( IMP anImp );
// 解除block與IMP(使用imp_implementationWithBlock創(chuàng)建的)的關(guān)聯(lián)關(guān)系苛聘,并釋放block的拷貝
BOOL imp_removeBlock ( IMP anImp );
測(cè)試代碼
@interface MyRuntimeBlock : NSObject
@end
@implementation MyRuntimeBlock
@end
IMP imp = imp_implementationWithBlock(^(id obj, NSString *str) {
NSLog(@"%@", str);
});
class_addMethod(MyRuntimeBlock.class, @selector(testBlock:), imp, "v@:@");
MyRuntimeBlock *runtime = [[MyRuntimeBlock alloc] init];
[runtime performSelector:@selector(testBlock:) withObject:@"hello world!"];
//結(jié)果
2014-11-09 14:03:19.779 [1172:395446] hello world!
Runtime的應(yīng)用
獲取系統(tǒng)提供的庫(kù)相關(guān)信息
主要函數(shù)
// 獲取所有加載的Objective-C框架和動(dòng)態(tài)庫(kù)的名稱(chēng)
const char ** objc_copyImageNames ( unsigned int *outCount );
// 獲取指定類(lèi)所在動(dòng)態(tài)庫(kù)
const char * class_getImageName ( Class cls );
// 獲取指定庫(kù)或框架中所有類(lèi)的類(lèi)名
const char ** objc_copyClassNamesForImage ( const char *image, unsigned int *outCount );
通過(guò)這些函數(shù)就能夠獲取某個(gè)類(lèi)所有的庫(kù),以及某個(gè)庫(kù)中包含哪些類(lèi)
NSLog(@"獲取指定類(lèi)所在動(dòng)態(tài)庫(kù)");
NSLog(@"UIView's Framework: %s", class_getImageName(NSClassFromString(@"UIView")));
NSLog(@"獲取指定庫(kù)或框架中所有類(lèi)的類(lèi)名");
const char ** classes = objc_copyClassNamesForImage(class_getImageName(NSClassFromString(@"UIView")), &outCount);
for (int i = 0; i < outCount; i++) {
NSLog(@"class name: %s", classes[i]);
}
//結(jié)果
2014-11-08 12:57:32.689 [747:184013] 獲取指定類(lèi)所在動(dòng)態(tài)庫(kù)
2014-11-08 12:57:32.690 [747:184013] UIView's Framework: /System/Library/Frameworks/UIKit.framework/UIKit
2014-11-08 12:57:32.690 [747:184013] 獲取指定庫(kù)或框架中所有類(lèi)的類(lèi)名
2014-11-08 12:57:32.691 [747:184013] class name: UIKeyboardPredictiveSettings
2014-11-08 12:57:32.691 [747:184013] class name: _UIPickerViewTopFrame
2014-11-08 12:57:32.691 [747:184013] class name: _UIOnePartImageView
2014-11-08 12:57:32.692 [747:184013] class name: _UIPickerViewSelectionBar
2014-11-08 12:57:32.692 [747:184013] class name: _UIPickerWheelView
2014-11-08 12:57:32.692 [747:184013] class name: _UIPickerViewTestParameters
......
對(duì)App的用戶(hù)行為進(jìn)行追蹤
就是用戶(hù)點(diǎn)擊時(shí)把事件記錄下來(lái)忠聚。一般比較做法就是在viewDidAppear里記錄事件设哗,這樣會(huì)讓這樣記錄事件的代碼遍布整個(gè)項(xiàng)目中。繼承或類(lèi)別也會(huì)有問(wèn)題两蟀。這時(shí)利用Method Swizzling把一個(gè)方法的實(shí)現(xiàn)和另一個(gè)方法的實(shí)現(xiàn)進(jìn)行替換网梢。
//先定義一個(gè)類(lèi)別,添加要Swizzled的方法
@implementation UIViewController (Logging)- (void)swizzled_viewDidAppear:(BOOL)animated
{ // call original implementation
[self swizzled_viewDidAppear:animated]; // Logging
[Logging logWithEventName:NSStringFromClass([self class])];
}
//接下來(lái)實(shí)現(xiàn)swizzle方法
@implementation UIViewController (Logging)void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) { // the method might not exist in the class, but in its superclass
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); // class_addMethod will fail if original method already exists
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); // the method doesn’t exist and we just added one
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
//最后要確保在程序啟動(dòng)的時(shí)候調(diào)用swizzleMethod方法在之前的UIViewController的Logging類(lèi)別里添加+load:方法赂毯,然后在+load:里把viewDidAppear替換掉
@implementation UIViewController (Logging)+ (void)load
{
swizzleMethod([self class], @selector(viewDidAppear:), @selector(swizzled_viewDidAppear:));
}
//更簡(jiǎn)化直接用新的IMP取代原IMP战虏,不是替換,只需要有全局的函數(shù)指針指向原IMP即可党涕。
void (gOriginalViewDidAppear)(id, SEL, BOOL);void newViewDidAppear(UIViewController *self, SEL _cmd, BOOL animated)
{ // call original implementation
gOriginalViewDidAppear(self, _cmd, animated); // Logging
[Logging logWithEventName:NSStringFromClass([self class])];
}
+ (void)load
{
Method originalMethod = class_getInstanceMethod(self, @selector(viewDidAppear:));
gOriginalViewDidAppear = (void *)method_getImplementation(originalMethod); if(!class_addMethod(self, @selector(viewDidAppear:), (IMP) newViewDidAppear, method_getTypeEncoding(originalMethod))) {
method_setImplementation(originalMethod, (IMP) newViewDidAppear);
}
}
通過(guò)Method Swizzling可以把事件代碼或Logging活烙,Authentication,Caching等跟主要業(yè)務(wù)邏輯代碼解耦遣鼓。這種處理方式叫做Cross Cutting Concernshttp://en.wikipedia.org/wiki/Cross-cutting_concern 用Method Swizzling動(dòng)態(tài)給指定的方法添加代碼解決Cross Cutting Concerns的編程方式叫Aspect Oriented Programming http://en.wikipedia.org/wiki/Aspect-oriented_programming 目前有些第三方庫(kù)可以很方便的使用AOP啸盏,比如Aspects https://github.com/steipete/Aspects 這里是使用Aspects的范例https://github.com/okcomp/AspectsDemo