Objc Runtime 深入學(xué)習(xí)類(lèi),對(duì)象器一,Method课锌,消息,Protocol祈秕,Category和Block的底層結(jié)構(gòu)和運(yùn)行時(shí)操作函數(shù)

概述

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消息的參考文章

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的資源可以參考

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骑祟,隨后出現(xiàn)的幾起案子回懦,更是在濱河造成了極大的恐慌气笙,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怯晕,死亡現(xiàn)場(chǎng)離奇詭異潜圃,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)舟茶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)谭期,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吧凉,你說(shuō)我怎么就攤上這事隧出。” “怎么了阀捅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵胀瞪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我饲鄙,道長(zhǎng)凄诞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任忍级,我火速辦了婚禮帆谍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘轴咱。我一直安慰自己既忆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布嗦玖。 她就那樣靜靜地躺著患雇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宇挫。 梳的紋絲不亂的頭發(fā)上苛吱,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音器瘪,去河邊找鬼翠储。 笑死,一個(gè)胖子當(dāng)著我的面吹牛橡疼,可吹牛的內(nèi)容都是我干的援所。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼欣除,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼住拭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤滔岳,失蹤者是張志新(化名)和其女友劉穎杠娱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谱煤,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摊求,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刘离。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片室叉。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖硫惕,靈堂內(nèi)的尸體忽然破棺而出茧痕,到底是詐尸還是另有隱情,我是刑警寧澤疲憋,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布凿渊,位于F島的核電站梁只,受9級(jí)特大地震影響缚柳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搪锣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一秋忙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧构舟,春花似錦灰追、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至努咐,卻和暖如春苦蒿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渗稍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工佩迟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人竿屹。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓报强,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拱燃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秉溉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評(píng)論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,544評(píng)論 33 466
  • 原文出處:南峰子的技術(shù)博客 Objective-C語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言,它將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事放到了...
    _燴面_閱讀 1,215評(píng)論 1 5
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 727評(píng)論 0 2
  • Objective-C語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言坚嗜,他將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事情放到了運(yùn)行時(shí)來(lái)處理夯膀。這種動(dòng)態(tài)語(yǔ)言...
    tigger丨閱讀 1,381評(píng)論 0 8