iOS-Runtime在開發(fā)中的使用及相關(guān)面試題

OC語言中最為強(qiáng)大的莫過于OC的運(yùn)行時(shí)機(jī)制-Runtime,但因其比較接近底層,一旦使用Runtime出現(xiàn)bug,將很難調(diào)試,所以Runtime在開發(fā)中能不用就不用.下面我將介紹一些Runtime在開發(fā)中的使用,已經(jīng)面試可能遇見的面試題.

1.OC語法和Runtime語法的區(qū)別

OC語法和Runtime語法的區(qū)別,換而言之就是OC中我們寫的語句,最終被轉(zhuǎn)換成Runtime中什么樣語句.由于Xcode6之后,蘋果不建議使用Runtime,也就是現(xiàn)在在編譯的時(shí)候,runtime的函數(shù)不會(huì)提示,需要去配置一下:

// 配置步驟: build Seting -> 搜索msg -> 設(shè)置成NO

創(chuàng)建一個(gè)控制臺(tái)程序,在自動(dòng)釋放池中寫如下代碼:

NSObject *objc = [NSObject alloc];

? ? ? ? objc = [objc init];

然后切換到終端命令行,執(zhí)行以下步驟:

cd 切換到你想生成的那個(gè)根文件的上一級(jí)目錄

clang -rewrite-objc main.m// clang -rewrite-objc 目標(biāo)文件

會(huì)在該目錄文件下生成一個(gè).cpp文件,打開之后搜索@autoreleasepool(這也就是當(dāng)時(shí)為什么創(chuàng)建控制器程序的原因,好查找轉(zhuǎn)換后的代碼在哪兒),就會(huì)找到轉(zhuǎn)換后的代碼:??


NSObject *objc = ((NSObject *(*)(id, SEL))(void*)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));

objc = ((NSObject *(*)(id, SEL))(void*)objc_msgSend)((id)objc, sel_registerName("init"));

上面的代碼比較原生態(tài),我們要是直接寫runtime的代碼如下所示,就能達(dá)到創(chuàng)建一個(gè)NSObject對(duì)象的目的:

// objc_msgSend: 兩個(gè)參數(shù)? 1. 誰發(fā)送這個(gè)消息? 2.? 發(fā)送給誰NSObject *objc =? objc_msgSend([NSObjectclass], @selector(alloc));

? ? ? ? objc = objc_msgSend(objc, @selector(init));

2.消息機(jī)制,調(diào)用私有方法

?面試題: ?runtime是什么?或者是同類的

 答: 其實(shí)runtime就是運(yùn)行時(shí)機(jī)制,可以通過命令行clang -rewrite-objc 對(duì)應(yīng)的目標(biāo)文件,就能將對(duì)應(yīng)的OC的代碼轉(zhuǎn)成對(duì)應(yīng)的運(yùn)行時(shí)的代碼

若是面試官問runtime中是怎么找到對(duì)應(yīng)的方法的,該怎么回答?

 答: 首先確定問的是對(duì)象方法還是類方法,對(duì)象方法保存到類中,類方法保存到元類(meta class),每一個(gè)類都有方法列表methodList,每一個(gè)方法在方法列表中都有對(duì)應(yīng)的方法編號(hào).(1)根據(jù)對(duì)象的isa去對(duì)應(yīng)的類查找方法,isa: 判斷去哪個(gè)類找對(duì)應(yīng)的方法,指向方法調(diào)用的類 (2)根據(jù)傳入的方法編號(hào),才能在方法列表中找到對(duì)應(yīng)得方法Method(方法名).(3)根據(jù)方法名(函數(shù)入口)找到函數(shù)實(shí)現(xiàn)

知識(shí)擴(kuò)充: 其實(shí)每個(gè)方法最終轉(zhuǎn)換成函數(shù)的形式,存放在方法區(qū),而每一個(gè)函數(shù)的函數(shù)名都是函數(shù)的入口

訪問類中私有方法的代碼如下:

在對(duì)應(yīng)類中的@implementation實(shí)現(xiàn)私有方法:

#import"Person.h"@implementation Person- (void)eat {

? ? NSLog(@"吃吃吃");

}- (void)run: (int)num {

? ? NSLog(@"跑了%d米", num);

}@end

?在ViewController.m中的代碼如下:

#import"ViewController.h"#import"Person.h"#import/*? ? runtime: 千萬不要隨便使用,不得已才使用

消息機(jī)制:

1. 裝逼

2. 調(diào)用已知私有的方法

*/@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {


? ? Person *p = objc_msgSend([Personclass], @selector(alloc));


? ? p = objc_msgSend(p, @selector(init));

? ? //? ? objc_msgSend(p, @selector(eat));objc_msgSend(p, @selector(run:),20);

}@end

注意: 一定要導(dǎo)入runtime的頭文件 :

#include 或者#import

?3.runtime方法交換

需求1: 我現(xiàn)在有一個(gè)項(xiàng)目,已經(jīng)開發(fā)了兩年,之前都是用UIImage中的imageNamed去加載圖片,但是組長(zhǎng)現(xiàn)在想imageNamed,給我提示是否加載成功.

思想1:在分類實(shí)現(xiàn)該方法.(但這種方法會(huì)把系統(tǒng)的方法覆蓋,一般不采用)

思想2: 自定義一個(gè)Image類,為什么不采用這種方法(這里你就要明白什么時(shí)候需要自定義,系統(tǒng)功能不完善,就定義這樣一個(gè)類,去擴(kuò)展這個(gè)類)

前兩種方法都有一定的局限性,若是項(xiàng)目開發(fā)很久了,就需要更改好多東西,利用runtime交換方法實(shí)現(xiàn)的作用,可以簡(jiǎn)單的實(shí)現(xiàn)這個(gè)需求


這個(gè)時(shí)候不得不用runtime去交換方法

分類中代碼如下UIImage+image.h

#import@interface UIImage (image)+ (UIImage *)BO_imageNamed:(NSString *)name;@end

分類中代碼如下UIImage+image.m

#import"UIImage+image.h"#import@implementation UIImage (image)//如果當(dāng)前類中東西僅且只需加載一次,一般放在load中.當(dāng)然也可以放在initialize中,需要進(jìn)行判斷調(diào)用該類的是的類的類型// 加載類的時(shí)候會(huì)調(diào)用,僅且調(diào)用一次+ (void)load {

? ? ? ? // 首先要拿到要交換的兩個(gè)方法Method method1 = class_getClassMethod([UIImageclass], @selector(BO_imageNamed:));

? ? Method method2 = class_getClassMethod([UIImageclass], @selector(imageNamed:));

? ? method_exchangeImplementations(method1, method2);

}// 加載當(dāng)前類或者子類時(shí)候.會(huì)調(diào)用.可能會(huì)調(diào)用不止一次+ (void)initialize? {


}// 在系統(tǒng)方法的之前加前綴名的作用,防止覆蓋系統(tǒng)方法,有開發(fā)經(jīng)驗(yàn)的人默認(rèn)的+ (UIImage *)BO_imageNamed:(NSString *)name{

? ? // 當(dāng)運(yùn)行到這兒時(shí),這里已經(jīng)是imageNamed中的內(nèi)容,此時(shí)再調(diào)用BO_imageNamed相當(dāng)于原來imageNamed中的內(nèi)容UIImage *image = [self BO_imageNamed:name];


? ? if(image == nil) {

? ? ? ? NSLog(@"照片不存在");

? ? }


? ? return image;

}@end

調(diào)用的代碼如下:

#import"ViewController.h"http://#import "BOImage.h"#import"UIImage+image.h"/*? ? 需求: 不得不用runtime去交換方法

? ? 需求: 想要在調(diào)用imageNamed,就給我提示,是否加載成功

? ? 需求: 讓UIImage調(diào)用imageNamed有這個(gè)功能

? ? 需求: 比如我有一個(gè)項(xiàng)目,已經(jīng)開發(fā)兩年,之前都是用UIImage去加載圖片.組長(zhǎng)現(xiàn)在想調(diào)用imageNamed,就給我提示,是否加載成功


? ? 注意: 在分類中一定不要重寫系統(tǒng)方法,否則就把系統(tǒng)方法干掉了

? ? 思想: 什么時(shí)候需要自定義,系統(tǒng)功能不完善,就定義一個(gè)這樣的類,去擴(kuò)展這個(gè)類

//? ? 前兩種方法都有一定的局限性,若是項(xiàng)目開發(fā)很久了,則需要更改好多東西,利用runtime交換方法實(shí)現(xiàn)的作用.可以簡(jiǎn)單的實(shí)現(xiàn)這個(gè)需求

*/@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {

? ? [super viewDidLoad];

? ? //? ? [BOImage imageNamed:@"123"];[UIImage BO_imageNamed:@"123"];


}- (void)didReceiveMemoryWarning {

? ? [super didReceiveMemoryWarning];

? ? // Dispose of any resources that can be recreated.}@end

4: 動(dòng)態(tài)添加方法

應(yīng)用場(chǎng)景:

  為什么動(dòng)態(tài)添加方法?OC中是懶加載,有的方法可能很久不會(huì)調(diào)用,例如: 電商,視頻,社交,收費(fèi)項(xiàng)目,會(huì)員機(jī)制,只有會(huì)員才擁有這些動(dòng)能

下面是道美團(tuán)面試題:

面試官問: 有沒有使用過performSelector----->其實(shí)這里面試官想問的是你有沒有動(dòng)態(tài)的添加過方法

  這里就應(yīng)該這樣答: 使用過--->什么時(shí)候使用----動(dòng)態(tài)添加方法的時(shí)候使用--->為什么動(dòng)態(tài)添加方法---又回到到上面說的什么時(shí)候動(dòng)態(tài)添加方法.

代碼如下:

#import"Person.h"#import@implementation Personvoideat(id self, SEL _cmd) {

? ? NSLog(@"我終于成功了");

}// 動(dòng)態(tài)添加實(shí)例方法//resolveInstanceMethod 什么時(shí)候調(diào)用?只要調(diào)用沒有實(shí)現(xiàn)的方法,就會(huì)產(chǎn)生方法去解決,這個(gè)方法有什么作用: 去解決沒有實(shí)現(xiàn)方法,動(dòng)態(tài)添加方法+ (BOOL)resolveInstanceMethod:(SEL)sel {


? ? if(sel == @selector(eat)) {


? ? ? ? /**

? ? ? ? 給一個(gè)類添加方法

? ? ? ? @param self 給誰添加方法

? ? ? ? @param sel 添加那個(gè)方法

? ? ? ? @param IMP 方法實(shí)現(xiàn),函數(shù)入口

? ? ? ? @return 方法類型

? ? ? ? */? ? ? ? class_addMethod(self, sel, (IMP)eat, "v@:");

? ? }

? ? return [super resolveInstanceMethod:sel];

}// 動(dòng)態(tài)添加類方法//+ (BOOL)resolveClassMethod:(SEL)sel {////}@end// 下面是各個(gè)字母代表的參數(shù)//c? A char//i? An int//s? A short//l? A long//l? is treated as a 32-bit quantity on 64-bit programs.//q? A long long//C? An unsigned char//I? An unsigned int//S? An unsigned short//L? An unsigned long//Q? An unsigned long long//f? A float//d? A double//B? A C++ bool or a C99 _Bool//v? A void//*? A character string (char *)//@? An object (whether statically typed or typed id)//#? A class object (Class)//:? A method selector (SEL)//[array type]? An array//{name=type...}? A structure//? (name=type...) A union// bnum A bit field of num bits//^type? A pointer to type// ?? An unknown type (among other things, this code is used for function pointers)

控制器中方法如下:

#import"ViewController.h"#import"Person.h"/*? ? 動(dòng)態(tài)添加方法:

? ? 為什么動(dòng)態(tài)添加方法? OC都是懶加載,有些方法可能很久不會(huì)調(diào)用.例如: 電商,視頻,社交,收費(fèi)項(xiàng)目,會(huì)員機(jī)制,只有會(huì)員才擁有這些動(dòng)能

美團(tuán)面試題 : 有沒有使用過performSelector,使用,什么時(shí)候使用,動(dòng)態(tài)添加方法的時(shí)候使用,為什么動(dòng)態(tài)添加方法?

OC都是懶加載,有些方法可能很久不會(huì)調(diào)用.例如: 電商,視頻,社交,收費(fèi)項(xiàng)目,會(huì)員機(jī)制,只有會(huì)員才擁有這些動(dòng)能

*/@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {

? ? [super viewDidLoad];


? ? Person *p = [[Person alloc] init];


? ? [p performSelector:@selector(eat)];

}- (void)didReceiveMemoryWarning {

? ? [super didReceiveMemoryWarning];

? ? // Dispose of any resources that can be recreated.}@end

5.動(dòng)態(tài)添加屬性

理論上在分類中@property的作用: 僅僅是生成get,set方法的聲明,并不會(huì)生成get,set方法實(shí)現(xiàn),并不會(huì)生成下劃線屬性

動(dòng)態(tài)添加方法實(shí)現(xiàn)思路: 在分類中用@property添加set,get方法之后,其實(shí)添加屬性就是要把一個(gè)變量跟一個(gè)類聯(lián)系起來.也就是在set和get方法中處理,代碼如下所示.

給NSObject添加一個(gè)name屬性:

分類中代碼 .h:

#import@interface NSObject (Property)// @property 在分類中作用 : 僅僅是生成get,set方法聲明.并不會(huì)生成get,set方法實(shí)現(xiàn),并不會(huì)生成下劃線成員屬性@property NSString *name;@end

.m

#import"NSObject+Property.h"#import@implementation NSObject (Property)- (void)setName:(NSString *)name {

? ? objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}- (NSString *)name {

? ? returnobjc_getAssociatedObject(self,"name");

}@end

控制器中代碼:

#import"ViewController.h"#import"NSObject+Property.h"/*? ? 開發(fā)的時(shí)候,是自己最熟悉什么用什么,而不是什么逼格高用什么,rumtime比較接近底層的語言,不好調(diào)試,盡量少用

? ? 需求: 給NSObject添加一個(gè)name屬性,動(dòng)態(tài)添加屬性 ->runtime

? ? 屬性的本質(zhì): 讓一個(gè)屬性和對(duì)象產(chǎn)生關(guān)聯(lián)

*/@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {

? ? [super viewDidLoad];


? ? NSObject *objc = [[NSObject alloc] init];


? ? objc.name =@"123";


? ? NSLog(@"%@", objc.name);

}@end

6:利用運(yùn)行時(shí),自己添加屬性

如果一個(gè)字典中,有很多的key,如果你在字典轉(zhuǎn)模型的時(shí)候,逐個(gè)的寫下屬性,將會(huì)非常蛋疼,其實(shí)可以給字典添加一個(gè)分類,利用遍歷字典中key,value,再利用字符串的拼接即可實(shí)現(xiàn).

NSDictionary+propertyCode.h分類中代碼如下:

#import@interface NSDictionary (propertyCode)- (void)createProperty;@end

NSDictionary+propertyCode.m:

#import"NSDictionary+propertyCode.h"@implementation NSDictionary (propertyCode)- (void)createProperty {


? ? [self enumerateKeysAndObjectsUsingBlock:^(id_Nonnull key,id_Nonnull value, BOOL * _Nonnull stop) {

? ? ? ? // 當(dāng)然這里還是可以自己添加其他類型,就不一一列舉if([value isKindOfClass:[NSStringclass]]) {

? ? ? ? ? ? NSLog(@"%@", [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@", key]);

? ? ? ? }elseif([value isKindOfClass:[NSArrayclass]]) {

? ? ? ? ? ? NSLog(@"%@", [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@", key]);

? ? ? ? }elseif([value isKindOfClass:[NSNumberclass]]) {

? ? ? ? ? ? NSLog(@"%@", [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger key"]);

? ? ? ? }



? ? }];

}@end

控制器中代碼:

#import"ViewController.h"#import"NSDictionary+propertyCode.h"@interface ViewController ()

@property (nonatomic, strong) NSArray *array;@end@implementation ViewController- (NSArray *)array {

? ? if(_array == nil) {

? ? ? ? _array = [NSArray array];

? ? }

? ? return _array;

}- (void)viewDidLoad {

? ? [super viewDidLoad];


? ? // 這里需要拿到一個(gè)plist文件或者一個(gè)設(shè)置一個(gè)字典self.array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"cars.plist" ofType:nil]];

? ? for(NSInteger i =0; i < self.array.count; i++) {


? ? ? ? NSDictionary *dict = self.array[i];


? ? ? ? [dict createProperty];

? ? }//? ? NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"" ofType:nil]];}@end

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牍蜂,老刑警劉巖佛掖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赏参,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乏盐,警方通過查閱死者的電腦和手機(jī)汹忠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門淋硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人错维,你說我怎么就攤上這事奖地。” “怎么了赋焕?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)仰楚。 經(jīng)常有香客問我隆判,道長(zhǎng),這世上最難降的妖魔是什么僧界? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任侨嘀,我火速辦了婚禮,結(jié)果婚禮上捂襟,老公的妹妹穿的比我還像新娘咬腕。我一直安慰自己,他們只是感情好葬荷,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布涨共。 她就那樣靜靜地躺著纽帖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪举反。 梳的紋絲不亂的頭發(fā)上懊直,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音火鼻,去河邊找鬼室囊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛魁索,可吹牛的內(nèi)容都是我干的融撞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼粗蔚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼懦铺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起支鸡,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤冬念,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后牧挣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體急前,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年瀑构,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裆针。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寺晌,死狀恐怖世吨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呻征,我是刑警寧澤耘婚,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站陆赋,受9級(jí)特大地震影響沐祷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜攒岛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一赖临、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灾锯,春花似錦兢榨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凌那。三九已至,卻和暖如春暖璧,著一層夾襖步出監(jiān)牢的瞬間案怯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工澎办, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘲碱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓局蚀,卻偏偏與公主長(zhǎng)得像麦锯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子琅绅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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