亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

IOS Object-C 中Runtime詳解及實例代碼

 更新時間:2017年03月08日 14:41:23   投稿:lqh  
這篇文章主要介紹了IOS Object-C 中Runtime詳解及實例代碼的相關(guān)資料,OC中的對象其實在Runtime中都會用結(jié)構(gòu)體來表示,這個結(jié)構(gòu)體中包含了類名、成員變量列表、方法列表、協(xié)議列表、緩存等,需要的朋友可以參考下

IOS Object-C 中Runtime詳解

最近了解了一下OC的Runtime,真的是OC中很強(qiáng)大的一個機(jī)制,看起來比較底層,但其實可以有很多活用的方式。

什么是Runtime

我們雖然是用Objective-C寫的代碼,其實在運(yùn)行過程中都會被轉(zhuǎn)化成C代碼去執(zhí)行。比如說OC的方法調(diào)用都會轉(zhuǎn)成C函數(shù) id objc_msgSend ( id self, SEL op, … ); 而OC中的對象其實在Runtime中都會用結(jié)構(gòu)體來表示,這個結(jié)構(gòu)體中包含了類名、成員變量列表、方法列表、協(xié)議列表、緩存等。

類在Runtime中的表示:

struct objc_class {
 Class isa;//指針,顧名思義,表示是一個什么,
 //實例的isa指向類對象,類對象的isa指向元類

#if !__OBJC2__
 Class super_class; //指向父類
 const char *name; //類名
 long version;
 long info;
 long instance_size
 struct objc_ivar_list *ivars //成員變量列表
 struct objc_method_list **methodLists; //方法列表
 struct objc_cache *cache;//緩存
 //一種優(yōu)化,調(diào)用過的方法存入緩存列表,下次調(diào)用先找緩存
 struct objc_protocol_list *protocols //協(xié)議列表
 #endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

整個Runtime機(jī)制其實可以挖的點很多,這里只是簡單的介紹一些常見的用法,如果將其細(xì)細(xì)解析,相信一定會對OC的理解加深幾個層面。

獲取屬性/方法/協(xié)議列表

最直接的一種用法,就是獲取我們的結(jié)構(gòu)體中存儲的對象的屬性、方法、協(xié)議等列表,從而獲取其所有這些信息。

要獲取也比較簡單,但是自己嘗試之前需要注意幾點:

一定要自己給類加幾個屬性、方法,遵循一些協(xié)議,否則當(dāng)然是看不到輸出信息的。

要使用這些獲取的方法,需要導(dǎo)入頭文件 #import

#import <objc/runtime.h>

// 輸出類的一些信息
- (void)logInfo {
 unsigned int count;// 用于記錄列表內(nèi)的數(shù)量,進(jìn)行循環(huán)輸出

 // 獲取屬性列表
 objc_property_t *propertyList = class_copyPropertyList([self class], &count);
 for (unsigned int i = 0; i < count; i++) {
 const char *propertyName = property_getName(propertyList[i]);
 NSLog(@"property --> %@", [NSString stringWithUTF8String:propertyName]);
 }

 // 獲取方法列表
 Method *methodList = class_copyMethodList([self class], &count);
 for (unsigned int i; i < count; i++) {
 Method method = methodList[i];
 NSLog(@"method --> %@", NSStringFromSelector(method_getName(method)));
 }

 // 獲取成員變量列表
 Ivar *ivarList = class_copyIvarList([self class], &count);
 for (unsigned int i; i < count; i++) {
 Ivar myIvar = ivarList[i];
 const char *ivarName = ivar_getName(myIvar);
 NSLog(@"Ivar --> %@", [NSString stringWithUTF8String:ivarName]);
 }

 // 獲取協(xié)議列表
 __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
 for (unsigned int i; i < count; i++) {
 Protocol *myProtocal = protocolList[i];
 const char *protocolName = protocol_getName(myProtocal);
 NSLog(@"protocol --> %@", [NSString stringWithUTF8String:protocolName]);
 }
}

方法調(diào)用的過程

調(diào)用方法分為調(diào)用實例方法和調(diào)用類方法,在結(jié)構(gòu)體我們可以看到第一個就是一個 isa 指針,會指向類對象或者元類,什么叫元類呢?

每個實例對象有個isa的指針,他指向?qū)ο蟮念悾惱镆灿袀€isa的指針, 指向meteClass(元類)。元類保存了類方法的列表。當(dāng)類方法被調(diào)用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass)。根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)。

通過isa,就可以不斷往上方去回溯自己的父類等,而方法的調(diào)用也利用了這個過程:

  1. 首先,當(dāng)然在對象自己緩存的方法列表中去找要調(diào)用的方法,找到了就直接執(zhí)行其實現(xiàn)。
  2. 緩存里沒找到,就去上面說的它的方法列表里找,找到了就執(zhí)行其實現(xiàn)。
  3. 還沒找到,說明這個類自己沒有了,就會通過isa去向其父類里執(zhí)行1、2。
  4. 如果找到了根類還沒找到,那么就是沒有了,會轉(zhuǎn)向一個攔截調(diào)用的方法,我們可以自己在攔截調(diào)用方法里面做一些處理。
  5. 如果沒有在攔截調(diào)用里做處理,那么就會報錯崩潰。

以上就是方法調(diào)用的過程。我們可以看到的是,所謂重寫父類方法,并不是抹除了父類方法,父類的方法還是存在的,只是我們在子類里面找到了就不會再去父類里找了,是這個層面的“覆蓋”。而我們熟悉的 super 調(diào)用父類的方法實現(xiàn),就是告知要去調(diào)用父類實現(xiàn)的標(biāo)識。

攔截調(diào)用與動態(tài)添加

上面說到了攔截調(diào)用,就是在所有地方都找不到方法的實現(xiàn)的話,會出發(fā)攔截調(diào)用,可以自己去做一些處理避免程序崩潰。那在什么地方做處理呢?NSObject有四個方法可以用來做處理:

// 調(diào)用不存在的類方法時觸發(fā),默認(rèn)返回NO,可以加上自己的處理后返回YES
+ (BOOL)resolveClassMethod:(SEL)sel;

// 調(diào)用不存在的實例方法時觸發(fā),默認(rèn)返回NO,可以加上自己的處理后返回YES
+ (BOOL)resolveInstanceMethod:(SEL)sel;

// 將調(diào)用的不存在的方法重定向到一個其他聲明了這個方法的類里去,返回那個類的target
- (id)forwardingTargetForSelector:(SEL)aSelector;

// 將調(diào)用的不存在的方法打包成 NSInvocation 給你,自己處理后調(diào)用 invokeWithTarget: 方法讓某個類來觸發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation;

假設(shè)我們成功攔截下來了,那我們可以做什么處理呢?這個其實就是根據(jù)具體情況看你要怎么處理了。但這里有一個久聞其名的東西就可以派上用場了:動態(tài)添加方法。

我們知道OC是動態(tài)的,也就是說很多東西它不是在編譯時去判斷,而是在運(yùn)行時去處理的,這其實帶來了很大的靈活性,比如這里我們對于一些不存在的方法的調(diào)用,就可以動態(tài)去添加上一個方法來代替我們要調(diào)用的方法。

比如我們添加一個Button,點擊事件我們關(guān)聯(lián)到一個沒有具體實現(xiàn)的實例方法,這種情況下如果不做任何處理那么點擊Button后一定會崩潰,但是如果我們攔截并動態(tài)添加一個方法來報警,就不會崩潰了:

@interface ViewController ()
@property (nonatomic, strong) UIButton *logBtn;

- (void)notHas;// 要調(diào)用的實例方法,沒有具體實現(xiàn)
@end

- (void)viewDidLoad {
 [super viewDidLoad];

 // 添加按鈕
 self.logBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 20)];
 [self.logBtn setTitle:@"測 試" forState:UIControlStateNormal];
 [self.logBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
 // 添加沒有實現(xiàn)的點擊事件
 [self.logBtn addTarget:self action:@selector(notHas) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:self.logBtn];

}

// 攔截對不存在的方法的調(diào)用
+ (BOOL)resolveInstanceMethod:(SEL)sel {
 NSLog(@"notFind!");
 // 給本類動態(tài)添加一個方法
 if ([NSStringFromSelector(sel) isEqualToString:@"notHas"]) {
 class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
 }
 // 注意要返回YES
 return YES;
}

// 要動態(tài)添加的方法,這是一個C方法
 void runAddMethod(id self, SEL _cmd, NSString *string) {
 NSLog(@"動態(tài)添加一個方法來提示");
}

按照上面的處理,點擊按鈕后就不會崩潰,而是轉(zhuǎn)到了對 runAddMethod 方法的調(diào)用。其實更明確地說,應(yīng)該是重現(xiàn)了對要調(diào)用的方法的實現(xiàn),將原本要調(diào)用的方法的實現(xiàn),改為了一個新的實現(xiàn)。class_addMethod 方法的第二個參數(shù)是要重寫的方法,這里用的就是傳進(jìn)來的參數(shù)sel,第三個參數(shù)就是重寫后的實現(xiàn)。第四個參數(shù)是方法的簽名。

關(guān)聯(lián)對象

什么叫關(guān)聯(lián)對象?說通俗一點,我們都知道用Category類別可以給一些已經(jīng)存在的,比如系統(tǒng)的類添加方法,但是不能添加新屬性,那怎么添加屬性呢?一種直接的方法是繼承,但如果只是為了添加一個屬性就去做一次繼承,還是有點重,這時候,就可以用關(guān)聯(lián)對象的方法。

有兩個相關(guān)的方法:

objc_setAssociatedObject 方法用來給類關(guān)聯(lián)一個屬性;
objc_getAssociatedObject 方法用來獲取之前關(guān)聯(lián)的屬性。

比如說給自己這個類關(guān)聯(lián)一個字符串:

 // 關(guān)聯(lián)對象
 static char associatedObjectKey;
 objc_setAssociatedObject(self, &associatedObjectKey, @"我就是要關(guān)聯(lián)的字符串對象內(nèi)容", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 NSString *theString = objc_getAssociatedObject(self, &associatedObjectKey);
 NSLog(@"關(guān)聯(lián)對象:%@", theString);

我們先給self關(guān)聯(lián)了一個字符串內(nèi)容,然后通過get方法獲取了關(guān)聯(lián)的字符串內(nèi)容,并輸出。

從代碼中其實也可以猜到各個參數(shù)的意思,self的參數(shù)就是要處理的類;associatedObjectKey 的參數(shù)其實就類似于key,用來標(biāo)識區(qū)分你要關(guān)聯(lián)的這個對象;第三個參數(shù)是要關(guān)聯(lián)的對象;第四個參數(shù)是關(guān)聯(lián)的策略,用命名就可以看出來全是在添加@property屬性時用到的一些修飾符,有五種策略:

enum {
 OBJC_ASSOCIATION_ASSIGN = 0,
 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
 OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
 OBJC_ASSOCIATION_RETAIN = 01401,
 OBJC_ASSOCIATION_COPY = 01403 
};

熟悉@property屬性修飾符的應(yīng)該能直接明白了,不熟悉的可以看這篇文章:傳送門:iOS中assign、retain、copy、weak、strong的區(qū)別以及nonatomic的含義

當(dāng)然,你也可以和類別一起用,創(chuàng)建兩個方法用來關(guān)聯(lián)和獲取對象,比如下面這樣:

//添加關(guān)聯(lián)對象
- (void)addAssociatedObject:(id)object{
 objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

//獲取關(guān)聯(lián)對象
- (id)getAssociatedObject{
 return objc_getAssociatedObject(self, _cmd);
}

這樣就既能通過Category類別來添加方法,用一起順便提供了對屬性的添加了。

結(jié)

以上是對Runtime的一點淺薄的理解和使用,Runtime的天地應(yīng)該是很廣闊的,也能挖出很多高級的使用方法來,對于理解OC的運(yùn)行機(jī)制是很有幫助的。

源碼下載:http://xiazai.jb51.net/201703/yuanma/RuntimeDemo-master(jb51.net).rar

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • iOS通過block在兩個頁面間傳值的方法

    iOS通過block在兩個頁面間傳值的方法

    不知道大家有沒有發(fā)現(xiàn),在實際開發(fā)中使用block的地方特別多,block比delegate和notification有著更簡潔的優(yōu)勢,下面這篇文章我們來簡單了解一下block在兩個頁面之間的傳值。有需要的朋友們可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2016-11-11
  • iOS開發(fā)實現(xiàn)圖片瀏覽功能

    iOS開發(fā)實現(xiàn)圖片瀏覽功能

    這篇文章主要為大家詳細(xì)介紹了iOS開發(fā)實現(xiàn)圖片瀏覽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • iOS實現(xiàn)毫秒倒計時的方法詳解

    iOS實現(xiàn)毫秒倒計時的方法詳解

    倒計時在我們?nèi)粘i_發(fā)中必不可少,最近在公司的一個項目中就遇到了這個需求,本文著重介紹的是利用iOS實現(xiàn)毫秒倒計時的方法,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-04-04
  • iOS runtime動態(tài)添加方法示例詳解

    iOS runtime動態(tài)添加方法示例詳解

    Runtime是想要做好iOS開發(fā),或者說是真正的深刻的掌握OC這門語言所必需理解的東西。下面這篇文章主要給大家介紹了關(guān)于iOS runtime動態(tài)添加方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2018-01-01
  • iOS 使用Moya網(wǎng)絡(luò)請求的實現(xiàn)方法

    iOS 使用Moya網(wǎng)絡(luò)請求的實現(xiàn)方法

    這篇文章主要介紹了iOS 使用Moya網(wǎng)絡(luò)請求的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • iOS仿微信添加標(biāo)簽效果(shape實現(xiàn))

    iOS仿微信添加標(biāo)簽效果(shape實現(xiàn))

    微信做的用戶體驗非常棒,今天用shape來做下微信的標(biāo)簽功能,非常不錯,對ios 仿微信添加標(biāo)簽功能感興趣的朋友一起看看吧
    2016-11-11
  • iOS實現(xiàn)波浪效果

    iOS實現(xiàn)波浪效果

    這篇文章主要為大家詳細(xì)介紹了iOS實現(xiàn)波浪效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • iOS 斷點上傳文件的實現(xiàn)方法

    iOS 斷點上傳文件的實現(xiàn)方法

    這項目開發(fā)中,有時候我們需要將本地的文件上傳到服務(wù)器,簡單的幾張圖片還好,但是針對iPhone里面的視頻文件進(jìn)行上傳,為了用戶體驗,我們有必要實現(xiàn)斷點上傳。這篇文章主要介紹了iOS 斷點上傳文件的實現(xiàn)方法,需要的朋友可以參考下
    2017-12-12
  • 詳解iOS中按鈕點擊事件處理方式

    詳解iOS中按鈕點擊事件處理方式

    在iOS開發(fā)中,時常會用到按鈕,通過按鈕的點擊來完成界面的跳轉(zhuǎn)等功能。具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • iOS掃描二維碼實現(xiàn)手勢拉近拉遠(yuǎn)鏡頭

    iOS掃描二維碼實現(xiàn)手勢拉近拉遠(yuǎn)鏡頭

    這篇文章主要為大家詳細(xì)介紹了iOS掃描二維碼實現(xiàn)手勢拉近拉遠(yuǎn)鏡頭,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-04-04

最新評論