OC之runtime


OC擁有很多的動態(tài)特性,這些動態(tài)特性在運行程序時發(fā)揮作用,而不是在編譯或者鏈接代碼時發(fā)揮作用.這一特性,也為OC提供了很多強大的功能和靈活性. 比如可以以實時的方式進行程序的開發(fā)和更新,而不需要重新編譯或者重新部署. 

運行時系統(tǒng)的基本應(yīng)用

對象消息

在面向?qū)ο笾心?消息傳遞是指在對象之間發(fā)送和接收消息. 在OC中的對象消息傳遞用于類和對象方法的調(diào)用.
[接收器 方法選擇器:(參數(shù))];
接收器就是class或者obj. 消息就是由 selecter 和 parameter構(gòu)成. 對象消息的傳遞是以動態(tài)的方式實現(xiàn)的. 接收器的相應(yīng)類型和相應(yīng)的調(diào)用方法,是在運行時決定的(所以會存在一些與動態(tài)綁定有關(guān)的額外開銷,當(dāng)然oc有緩存機制來減少這種開銷).

所以O(shè)C對象消息傳遞的關(guān)鍵要素有:

.接收器:類,對象

.消息:選擇器+參數(shù)
. 方法:聲明方法的實現(xiàn),參數(shù),返回值等等....
. 方法綁定. 向接收器發(fā)送消息的時候,尋找并且執(zhí)行方法的過程

選擇器
在消息傳遞的過程中,選擇器是一個文本字符串.
` -(void)addValue1:(NSInteger)value1 value2:(NSInteger)value2{...}`
`addValue1:value2:`

選擇器是一個特殊的類型.
SEL類型 是用于在編寫源代碼時替換選擇器值的標(biāo)識符.

- (void)viewDidLoad {
    [super viewDidLoad];
    // SEL addValue1 =  NSSelectorFromString(@"addvalue1:value2:");
    SEL addValue1 = @selector(addvalue1:value2:);
    //[self addvalue1:@"a" value2:@"b"];
    [self performSelector:addValue1 withObject:@"a" withObject:@"b"];
}

-(void)addvalue1:(NSString *)value1 value2:(NSString *)value2
{
 NSLog(@"%@",NSStringFromSelector(_cmd));
//addvalue1:value2:
}

動態(tài)類型 和 對象內(nèi)省

運行時系統(tǒng)通過動態(tài)類型(dynamic typing)功能可以在運行程序時,決定對象的類型.
id類型是一個OC所特有的類型,它的變量可以存儲任意類型的對象.

 id list = [NSArray new];
    list = @"I am a sting";
    if([list isKindOfClass:[NSArray class]]){}
    if([list isKindOfClass:[NSString class]]){}
    

好處就是可以簡化接口,提供非常大的靈活性.

動態(tài)綁定(dynamic binding)

是指在運行程序時(而不是編譯)將消息與方法對應(yīng)起來的處理過程.在運行程序和發(fā)送消息前,消息和接收消息的對象不會對應(yīng). 因為許多的接收器可能實現(xiàn)了相同的方法,調(diào)用方式會動態(tài)變化.動態(tài)綁定實現(xiàn)了 OOP的多態(tài)性.

    id person = [[People alloc]init];
    [person run];

因為person的類型為 People,運行時系統(tǒng)會搜尋 People的run方法. 如果沒有找到,就去父類中尋找對相應(yīng)方法.直到找到為止.

OC運行時系統(tǒng) 確定消息接收器的類型(動態(tài)類型)-> 然后確定實現(xiàn)的方法(動態(tài)綁定)->調(diào)用方法.

動態(tài)方法決議

動態(tài)方法決議能夠以動態(tài)的方法實現(xiàn)方法. 通過

+(BOOL)resolveClassMethod:(SEL)sel

+(BOOL)resolveInstanceMethod:(SEL)sel

Description: Dynamically provides an implementation for a given selector for an instance method.
This method and resolveClassMethod: allow you to dynamically provide an implementation for a given selector.
An Objective-C method is simply a C function that take at least two arguments—self and _cmd. Using the class_addMethod function, you can add a function to a class as a method. Given the following function:

parameters: name The name of a selector to resolve.

return: YES if the method was found and added to the receiver, otherwise NO.

以動態(tài)的方式,添加類方法,或者實例方法.

void doLogin(){
    NSLog(@"dologin");
}
@implementation ViewController

+(BOOL)resolveInstanceMethod:(SEL)sel
{
    NSString *string = NSStringFromSelector(sel);
    if ([string isEqualToString:@"doLogin"]) {
        class_addMethod([self class], sel, (IMP)doLogin,"@@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:NSSelectorFromString(@"doLogin")];
}

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id (*IMP)(id, SEL, ...); 
#endif

"@@:@" OC的類型編碼

-(id)forwardingTargetForSelector:(SEL)aSelector
Returns the object to which unrecognized messages should first be directed.
If an object implements (or inherits) this method, and returns a non-nil (and non-self) result, that returned object is used as the new receiver object and the message dispatch resumes to that new object.

       NSInteger a = [self performSelector:NSSelectorFromString(@"length")];
}
-(id)forwardingTargetForSelector:(SEL)aSelector
{
    if ([NSStringFromSelector(aSelector) isEqualToString:@"length"]) {
        return @"沒有找到方法";
    }
    return 0;
}

運行時系統(tǒng)的結(jié)構(gòu)

介紹運行時系統(tǒng)的結(jié)構(gòu)以及它實現(xiàn)這些動態(tài)特性的方式.

運行時系統(tǒng)的組成部分

OC的運行時系統(tǒng)由兩個部分組成:編譯器 和 運行時系統(tǒng)庫

編譯器

在編譯階段,編譯器對源文件進行一系列的處理(預(yù)處理,詞法分析,語法分析,鏈接...)等.生成構(gòu)成可執(zhí)行程序的二進制文件. 運行時系統(tǒng)會為OC的面向?qū)ο筇卣魈峁?biāo)準(zhǔn)的API和實現(xiàn)代碼. OC的面向?qū)ο笤睾蛣討B(tài)特性,都是由運行時系統(tǒng)提供的.
運行時系統(tǒng)由這幾部分組成:

類元素:接口,實現(xiàn)代碼,協(xié)議,分類,方法,屬性,變量等等
類實例: 對象
對象消息的傳遞: 動態(tài)類型,動態(tài)綁定
動態(tài)方法決議
動態(tài)加載
對象內(nèi)省

當(dāng)編譯器解析使用了這些元素和特性的OC源碼時,它就會使用適當(dāng)?shù)倪\行時系統(tǒng)庫數(shù)據(jù)結(jié)構(gòu)和函數(shù),生成可執(zhí)行代碼.例子:

  1. 生成對象消息傳遞代碼
    [接收器 消息];
    它會生成調(diào)用運行時系統(tǒng)庫中函數(shù) objc_msgSend()的代碼.將源碼轉(zhuǎn)化為objc_msgSend(..)的代碼,每條消息都是以動態(tài)的方式處理的,這就意味著接收器的類型和方法的實際實現(xiàn),都是在運行程序的時候決定的.
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
  1. 生成類和代碼
/// An opaque type that represents an Objective-C class.

typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};


Class的數(shù)據(jù)類型就是一個 objc_class 結(jié)構(gòu)體指針

 struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
 #if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

運行時庫提供接口,訪問這些信息:

    objc_getClass(const char *name); // 對象的類定義
    objc_getMetaClass(const char *name); // 對象的元類定義
    class_getSuperclass(__unsafe_unretained Class cls);  // 類的父類
    class_copyMethodList(__unsafe_unretained Class cls, unsigned int *outCount); // 類的方法列表
      class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount)//實例變量列表
    class_copyPropertyList(__unsafe_unretained Class cls, unsigned int *outCount);// 屬性列表

id 類型的定義

 typedef struct objc_object *id;

運行時系統(tǒng)庫

運行時系統(tǒng)庫提供的有公開API,這些API使用C語言編寫.例如:

  1. 使用運行時系統(tǒng)創(chuàng)建類.
NSString* hello(){
    return @"hello world";
}

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

   // 創(chuàng)建一個類
    Class dynaClass = objc_allocateClassPair([NSObject class], "DynaClass", 0);
    // 給類添加一個方法
    class_addMethod(dynaClass, @selector(firstMethod), (IMP)hello, "@@:@");
    objc_registerClassPair(dynaClass);
    id dynaObj = [[dynaClass alloc]init];
   id result =  [dynaObj performSelector:@selector(firstMethod)];
    NSLog(@"%@",result);
}

2.實現(xiàn)運行時系統(tǒng)的對象消息傳遞

運行時系統(tǒng)定義了一種方法數(shù)據(jù)類型(objc_method)

struct objc_method {
    SEL method_name    // SEL類型的變量                                      
    char *method_types                                       
    IMP method_imp    // IMP的變量 方法的函數(shù)地址                                       
}                 


typedef struct objc_method *Method;                                          

執(zhí)行方法的邏輯:
1.通過對象的isa指針,找到對象所屬的類
2.通過搜索類的方法緩存,查找方法的IMP指針.如果找到了,就去調(diào)用函數(shù).

  1. 類的方法列表
  2. 一系列的動態(tài)方法決議方法.
  3. 父類的方法列表
與運行時系統(tǒng)的交互
  1. 運行時系統(tǒng)api. C風(fēng)格的.
  2. NSObject提供了一套api.其他類繼承NSObject.
isKindOfClass..
performerSelect...

運行時系統(tǒng)的應(yīng)用

AFN里的方法混淆


+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));

    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
        af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }

    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
        af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
}



// AFN
static inline void af_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {
    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}



- (void)af_suspend {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_suspend]; // 方法交換后 [self suspend]; 掛起,結(jié)束
    
    if (state != NSURLSessionTaskStateSuspended) {
        // dataTask suspend 后發(fā)送通知
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}



//  
* @note This is an atomic version of the following:
 *  \code 
 *  IMP imp1 = method_getImplementation(m1);
 *  IMP imp2 = method_getImplementation(m2);
 *  method_setImplementation(m1, imp2);
 *  method_setImplementation(m2, imp1);
 *  \endcode
 */
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

YYModel

// 屬性列表
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
_propertyInfos = propertyInfos;
for (unsigned int i = 0; i < propertyCount; i++) {
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
if (info.name) propertyInfos[info.name] = info;
}
free(properties);
}



- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [super init];
_property = property;
const char *name = property_getName(property);
if (name) {
_name = [NSString stringWithUTF8String:name];
}

YYEncodingType type = 0;
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
switch (attrs[i].name[0]) {
case 'T': { // Type encoding
if (attrs[i].value) {

...

case 'v': return YYEncodingTypeVoid | qualifier;
case 'B': return YYEncodingTypeBool | qualifier;
case 'c': return YYEncodingTypeInt8 | qualifier;
case 'C': return YYEncodingTypeUInt8 | qualifier;
case 's': return YYEncodingTypeInt16 | qualifier;
case 'S': return YYEncodingTypeUInt16 | qualifier;
case 'i': return YYEncodingTypeInt32 | qualifier;
case 'I': return YYEncodingTypeUInt32 | qualifier;
case 'l': return YYEncodingTypeInt32 | qualifier;
case 'L': return YYEncodingTypeUInt32 | qualifier;
case 'q': return YYEncodingTypeInt64 | qualifier;
case 'Q': return YYEncodingTypeUInt64 | qualifier;
case 'f': return YYEncodingTypeFloat | qualifier;
case 'd': return YYEncodingTypeDouble | qualifier;
case 'D': return YYEncodingTypeLongDouble | qualifier;


}
....

case 'R': {
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': {
type |= YYEncodingTypePropertyCopy;
} break;
case '&': {
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': {
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;

-----------------------完----------------------

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贝攒,一起剝皮案震驚了整個濱河市馏艾,隨后出現(xiàn)的幾起案子欠啤,更是在濱河造成了極大的恐慌温鸽,老刑警劉巖审葬,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祥国,死亡現(xiàn)場離奇詭異粉臊,居然都是意外死亡荒典,警方通過查閱死者的電腦和手機意推,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門豆瘫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人菊值,你說我怎么就攤上這事外驱。” “怎么了腻窒?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵昵宇,是天一觀的道長。 經(jīng)常有香客問我儿子,道長瓦哎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任典徊,我火速辦了婚禮杭煎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卒落。我一直安慰自己羡铲,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布儡毕。 她就那樣靜靜地躺著也切,像睡著了一般扑媚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雷恃,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天疆股,我揣著相機與錄音,去河邊找鬼倒槐。 笑死旬痹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讨越。 我是一名探鬼主播两残,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼把跨!你這毒婦竟也來了人弓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤着逐,失蹤者是張志新(化名)和其女友劉穎崔赌,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耸别,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡健芭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了太雨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吟榴。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖囊扳,靈堂內(nèi)的尸體忽然破棺而出吩翻,到底是詐尸還是另有隱情,我是刑警寧澤锥咸,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布狭瞎,位于F島的核電站,受9級特大地震影響搏予,放射性物質(zhì)發(fā)生泄漏熊锭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一雪侥、第九天 我趴在偏房一處隱蔽的房頂上張望碗殷。 院中可真熱鬧,春花似錦速缨、人聲如沸锌妻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仿粹。三九已至搁吓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吭历,已是汗流浹背堕仔。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晌区,地道東北人摩骨。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像契讲,于是被迫代替她去往敵國和親仿吞。 傳聞我的和親對象是個殘疾皇子滑频,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理捡偏,服務(wù)發(fā)現(xiàn),斷路器峡迷,智...
    卡卡羅2017閱讀 134,671評論 18 139
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,990評論 6 13
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認(rèn)知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 1,939評論 1 3
  • 可愛的小萱萱银伟。 今天和爸爸一起, 在傳媒大學(xué)玩耍绘搞。 秋天的風(fēng)彤避,有些冷。 萱萱告訴爸爸“心里暖暖噠”
    顧小虎頭閱讀 308評論 0 0