iOS面試原理篇

runtime怎么添加屬性闲延、方法等

? ivar表示成員變量

?

class_addIvar

?

class_addMethod

?

class_addProperty

?

class_addProtocol

?

class_replaceProperty

是否可以把比較耗時的操作放在NSNotificationCenter中

?首先必須明確通知在哪個線程中發(fā)出陋葡,那么處理接受到通知的方法也在這個線程中調(diào)用

?如果在異步線程發(fā)的通知,那么可以執(zhí)行比較耗時的操作霞捡;

?如果在主線程發(fā)的通知讼撒,那么就不可以執(zhí)行比較耗時的操作

runtime如何實現(xiàn)weak屬性

?首先要搞清楚weak屬性的特點

weak策略表明該屬性定義了一種“非擁有關系” (nonowning relationship)。

為這種屬性設置新值時,設置方法既不保留新值煎娇,也不釋放舊值。此特質(zhì)同assign類似;

然而在屬性所指的對象遭到摧毀時贪染,屬性值也會清空(nil

out)

?那么runtime如何實現(xiàn)weak變量的自動置nil缓呛?

runtime對注冊的類,會進行布局杭隙,會將weak對象放入一個hash表中哟绊。

用weak指向的對象內(nèi)存地址作為key,當此對象的引用計數(shù)為0的時候會調(diào)用對象的dealloc方法痰憎,

假設weak指向的對象內(nèi)存地址是a票髓,那么就會以a為key,在這個weak hash表中搜索铣耘,找到所有以a為key的weak對象洽沟,從而設置為nil。

weak屬性需要在dealloc中置nil么

?在ARC環(huán)境無論是強指針還是弱指針都無需在dealloc設置為nil蜗细,ARC會自動幫我們處理

?即便是編譯器不幫我們做這些裆操,weak也不需要在dealloc中置nil

?在屬性所指的對象遭到摧毀時,屬性值也會清空

//模擬下weak的setter方法炉媒,大致如下

- (void)setObject:(NSObject *)object

{

objc_setAssociatedObject(self,"object", object,OBJC_ASSOCIATION_ASSIGN);

[object cyl_runAtDealloc:^{

_object =nil;

}];

}

一個Objective-C對象如何進行內(nèi)存布局踪区?(考慮有父類的情況)

?所有父類的成員變量和自己的成員變量都會存放在該對象所對應的存儲空間中

?父類的方法和自己的方法都會緩存在類對象的方法緩存中,類方法是緩存在元類對象中

?每一個對象內(nèi)部都有一個isa指針,指向他的類對象,類對象中存放著本對象的如下信息

○對象方法列表

○成員變量的列表

○屬性列表

每個Objective-C對象都有相同的結構吊骤,如下圖所示

Objective-C 對象的結構圖

ISA指針

根類(NSObject)的實例變量

倒數(shù)第二層父類的實例變量

...

父類的實例變量

類的實例變量

?根類對象就是NSObject缎岗,它的super class指針指向nil

類對象既然稱為對象,那它也是一個實例白粉。類對象中也有一個isa指針指向它的元類(metaclass)密强,即類對象是元類的實例茅郎。元類內(nèi)部存放的是類方法列表,根元類的isa指針指向自己或渤,superclass指針指向NSObject類

#一個objc對象的isa的指針指向什么系冗?有什么作用?

?每一個對象內(nèi)部都有一個isa指針薪鹦,這個指針是指向它的真實類型

?根據(jù)這個指針就能知道將來調(diào)用哪個類的方法

下面的代碼輸出什么掌敬?

@implementationSon :

Father

- (id)init

{

self= [superinit];

if(self) {

NSLog(@"%@",NSStringFromClass([self class]));

NSLog(@"%@",NSStringFromClass([super class]));

}

return

self;

}

@end

?答案:都輸出Son

?這個題目主要是考察關于objc中對self和super的理解:

○self是類的隱藏參數(shù),指向當前調(diào)用方法的這個類的實例池磁。而super本質(zhì)是一個編譯器標示符奔害,和self是指向的同一個消息接受者

○當使用self調(diào)用方法時,會從當前類的方法列表中開始找地熄,如果沒有华临,就從父類中再找;

○而當使用super時端考,則從父類的方法列表中開始找雅潭。然后調(diào)用父類的這個方法

○調(diào)用[self class]時,會轉(zhuǎn)化成objc_msgSend函數(shù)

idobjc_msgSend(id

self,SELop, ...)

○調(diào)用[super class]時却特,會轉(zhuǎn)化成objc_msgSendSuper函數(shù)

idobjc_msgSendSuper(structobjc_super *super,SELop, ...)

○第一個參數(shù)是objc_super這樣一個結構體扶供,其定義如下

structobjc_super {

__unsafe_unretained

idreceiver;

__unsafe_unretainedClass super_class;

};

○第一個成員是receiver,類似于上面的objc_msgSend函數(shù)第一個參數(shù)self

○第二個成員是記錄當前類的父類是什么,告訴程序從父類中開始找方法裂明,找到方法后椿浓,最后內(nèi)部是使用objc_msgSend(objc_super->receiver,@selector(class))去調(diào)用,

此時已經(jīng)和[self class]調(diào)用相同了闽晦,故上述輸出結果仍然返回Son

○ objc

Runtime開源代碼對- (Class)class方法的實現(xiàn)

-(Class)class{

returnobject_getClass(self);

}

runtime如何通過selector找到對應的IMP地址扳碍?(分別考慮類方法和實例方法)

?每一個類對象中都一個對象方法列表(對象方法緩存)

?類方法列表是存放在類對象中isa指針指向的元類對象中(類方法緩存)

?方法列表中每個方法結構體中記錄著方法的名稱,方法實現(xiàn),以及參數(shù)類型,其實selector本質(zhì)就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應的方法實現(xiàn).

?當我們發(fā)送一個消息給一個NSObject對象時仙蛉,這條消息會在對象的類對象方法列表里查找

?當我們發(fā)送一個消息給一個類時笋敞,這條消息會在類的Meta Class對象的方法列表里查找

objc中的類方法和實例方法有什么本質(zhì)區(qū)別和聯(lián)系

?類方法:

○類方法是屬于類對象的

○類方法只能通過類對象調(diào)用

○類方法中的self是類對象

○類方法可以調(diào)用其他的類方法

○類方法中不能訪問成員變量

○類方法中不能直接調(diào)用對象方法

○類方法是存儲在元類對象的方法緩存中

?實例方法:

○實例方法是屬于實例對象的

○實例方法只能通過實例對象調(diào)用

○實例方法中的self是實例對象

○實例方法中可以訪問成員變量

○實例方法中直接調(diào)用實例方法

○實例方法中可以調(diào)用類方法(通過類名)

○實例方法是存放在類對象的方法緩存中

使用runtime Associate方法關聯(lián)的對象,需要在主對象dealloc的時候釋放么捅儒?

?無論在MRC下還是ARC下均不需要

?被關聯(lián)的對象在生命周期內(nèi)要比對象本身釋放的晚很多液样,它們會在被NSObject -dealloc調(diào)用的object_dispose()方法中釋放

?補充:對象的內(nèi)存銷毀時間表振亮,分四個步驟

1.調(diào)用-release:引用計數(shù)變?yōu)榱?/p>

*對象正在被銷毀巧还,生命周期即將結束.

*不能再有新的__weak弱引用,否則將指向nil.

*調(diào)用[selfdealloc]

2.父類調(diào)用-dealloc

*繼承關系中最直接繼承的父類再調(diào)用-dealloc

*如果是MRC代碼 則會手動釋放實例變量們(iVars)

*繼承關系中每一層的父類 都再調(diào)用-dealloc

3.

NSObject調(diào)-dealloc

*只做一件事:調(diào)用Objective-C runtime中的object_dispose()方法

4.調(diào)用object_dispose()

*為C++的實例變量們(iVars)調(diào)用destructors

*為ARC狀態(tài)下的 實例變量們(iVars) 調(diào)用-release

*解除所有使用runtime Associate方法關聯(lián)的對象

*解除所有__weak引用

*調(diào)用free()

_objc_msgForward函數(shù)是做什么的坊秸?直接調(diào)用它將會發(fā)生什么麸祷?

?

_objc_msgForward是IMP類型,用于消息轉(zhuǎn)發(fā)的:當向一個對象發(fā)送一條消息褒搔,但它并沒有實現(xiàn)的時候阶牍,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)

?直接調(diào)用_objc_msgForward是非常危險的事喷面,這是把雙刃刀,如果用不好會直接導致程序Crash走孽,但是如果用得好惧辈,能做很多非常酷的事

?

JSPatch就是直接調(diào)用_objc_msgForward來實現(xiàn)其核心功能的

?詳細解說參見這里的第一個問題解答

能否向編譯后得到的類中增加實例變量磕瓷?能否向運行時創(chuàng)建的類中添加實例變量盒齿?為什么?

?不能向編譯后得到的類中增加實例變量困食;

?能向運行時創(chuàng)建的類中添加實例變量边翁;

?分析如下:

○因為編譯后的類已經(jīng)注冊在runtime中,類結構體中的objc_ivar_list實例變量的鏈表和instance_size實例變量的內(nèi)存大小已經(jīng)確定硕盹,同時runtime會調(diào)用class_setIvarLayout或class_setWeakIvarLayout來處理strong

weak引用符匾,所以不能向存在的類中添加實例變量

○運行時創(chuàng)建的類是可以添加實例變量,調(diào)用class_addIvar函數(shù)瘩例,但是得在調(diào)用objc_allocateClassPair之后啊胶,objc_registerClassPair之前,原因同上仰剿。

runloop和線程有什么關系创淡?

?每條線程都有唯一的一個RunLoop對象與之對應的

?主線程的RunLoop是自動創(chuàng)建并啟動

?子線程的RunLoop需要手動創(chuàng)建

?子線程的RunLoop創(chuàng)建步驟如下:

○在子線程中調(diào)用[NSRunLoop

currentRunLoop]創(chuàng)建RunLoop對象(懶加載,只創(chuàng)建一次)

○獲得RunLoop對象后要調(diào)用run方法來啟動一個運行循環(huán)

//啟動RunLoop

[[NSRunLoop

currentRunLoop] run];

RunLoop的其他啟動方法

//第一個參數(shù):指定運行模式

//第二個參數(shù):指定RunLoop的過期時間南吮,即:到了這個時間后RunLoop就失效了

[[NSRunLoop

currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate

distantFuture]];

runloop的mode作用是什么琳彩?

?用來控制一些特殊操作只能在指定模式下運行,一般可以通過指定操作的運行mode來控制執(zhí)行時機部凑,以提高用戶體驗

?系統(tǒng)默認注冊了5個Mode

kCFRunLoopDefaultMode:App的默認Mode露乏,通常主線程是在這個Mode下運行,對應OC中的:NSDefaultRunLoopMode

UITrackingRunLoopMode:界面跟蹤Mode涂邀,用于ScrollView追蹤觸摸滑動瘟仿,保證界面滑動時不受其他Mode影響

kCFRunLoopCommonModes:這是一個標記Mode,不是一種真正的Mode比勉,事件可以運行在所有標有common modes標記的模式中劳较,對應OC中的NSRunLoopCommonModes,帶有common modes標記的模式有:UITrackingRunLoopMode和kCFRunLoopDefaultMode

UIInitializationRunLoopMode:在啟動App時進入的第一個Mode浩聋,啟動完成后就不再使用

GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內(nèi)部Mode观蜗,通常用不到

以+scheduledTimerWithTimeInterval...的方式觸發(fā)的timer,在滑動頁面上的列表時衣洁,timer會暫定回調(diào)墓捻,為什么?如何解決坊夫?

?這里強調(diào)一點:在主線程中以+scheduledTimerWithTimeInterval...的方式觸發(fā)的timer默認是運行在NSDefaultRunLoopMode模式下的砖第,當滑動頁面上的列表時撤卢,進入了UITrackingRunLoopMode模式,這時候timer就會停止

?可以修改timer的運行模式為NSRunLoopCommonModes梧兼,這樣定時器就可以一直運行了

?以下是我的筆記補充:

○在子線程中通過scheduledTimerWithTimeInterval:...方法來構建NSTimer

§方法內(nèi)部已經(jīng)創(chuàng)建NSTimer對象放吩,并加入到RunLoop中,運行模式為NSDefaultRunLoopMode

§由于Mode有timer對象羽杰,所以RunLoop就開始監(jiān)聽定時器事件了屎慢,從而開始進入運行循環(huán)

§這個方法僅僅是創(chuàng)建RunLoop對象,并不會主動啟動RunLoop忽洛,需要再調(diào)用run方法來啟動

○如果在主線程中通過scheduledTimerWithTimeInterval:...方法來構建NSTimer腻惠,就不需要主動啟動RunLoop對象,因為主線程的RunLoop對象在程序運行起來就已經(jīng)被啟動了

// userInfo參數(shù):用來給NSTimer的userInfo屬性賦值欲虚,userInfo是只讀的集灌,只能在構建NSTimer對象時賦值

[NSTimer

scheduledTimerWithTimeInterval:1.0 target:selfselector:@selector(run:) userInfo:@"ya了個hoo"repeats:YES];

// scheduledTimer...方法創(chuàng)建出來NSTimer雖然已經(jīng)指定了默認模式,但是【允許你修改模式】

[[NSRunLoop

currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

//【僅在子線程】需要手動啟動RunLoop對象复哆,進入運行循環(huán)

[[NSRunLoop

currentRunLoop] run];

#猜想runloop內(nèi)部是如何實現(xiàn)的欣喧?

?從字面意思看:運行循環(huán)、跑圈梯找;

?本質(zhì):內(nèi)部就是do-while循環(huán)唆阿,在這個循環(huán)內(nèi)部不斷地處理各種事件(任務),比如:Source锈锤、Timer驯鳖、Observer;

?每條線程都有唯一一個RunLoop對象與之對應久免,主線程的RunLoop默認已經(jīng)啟動浅辙,子線程的RunLoop需要手動啟動;

?每次RunLoop啟動時阎姥,只能指定其中一個Mode记舆,這個Mode被稱作CurrentMode,如果需要切換Mode呼巴,只能退出Loop泽腮,再重新指定一個Mode進入,這樣做主要是為了隔離不同Mode中的Source衣赶、Timer诊赊、Observer,讓其互不影響屑埋;

?附上RunLoop的運行圖

不手動指定autoreleasepool的前提下豪筝,一個autorealese對象在什么時刻釋放痰滋?(比如在一個vc的viewDidLoad中創(chuàng)建)

?分兩種情況:手動干預釋放時機摘能、系統(tǒng)自動去釋放

○手動干預釋放時機:指定autoreleasepool就是所謂的:當前作用域大括號結束時就立即釋放

○系統(tǒng)自動去釋放:不手動指定autoreleasepool续崖,Autorelease對象會在當前的runloop迭代結束時釋放,下面詳細說明釋放時機

§ RunLoop中的三個狀態(tài)會處理自動釋放池团搞,通過打印代碼發(fā)現(xiàn)有兩個Observer監(jiān)聽到狀態(tài)值為:1和160(32+128)

□ kCFRunLoopEntry(1)//第一次進入會創(chuàng)建一個自動釋放池

□ kCFRunLoopBeforeWaiting(32)//進入休眠狀態(tài)前先銷毀自動釋放池严望,再創(chuàng)建一個新的自動釋放池

□ kCFRunLoopExit(128)//退出RunLoop時銷毀最后一次創(chuàng)建的自動釋放池

?如果在一個vc的viewDidLoad中創(chuàng)建一個Autorelease對象,那么該對象會在viewDidAppear方法執(zhí)行前就被銷毀了(是這樣的嗎逻恐?像吻??)

#蘋果是如何實現(xiàn)autoreleasepool的复隆?

? autoreleasepool以一個隊列數(shù)組的形式實現(xiàn),主要通過下列三個函數(shù)完成.

objc_autoreleasepoolPush

objc_autoreleasepoolPop

objc_aurorelease

?看函數(shù)名就可以知道拨匆,對autorelease分別執(zhí)行push,和pop操作挽拂。銷毀對象時執(zhí)行release操作

GCD的隊列(dispatch_queue_t)分哪兩種類型惭每?背后的線程模型是什么樣的?

?串行隊列

?并行隊列

? dispatch_global_queue();是全局并發(fā)隊列

? dispatch_main_queue();是一種特殊串行隊列

?背后的線程模型:自定義隊列dispatch_queue_t queue;可以自定義是并行:DISPATCH_QUEUE_CONCURRENT或者

串行DISPATCH_QUEUE_SERIAL

#蘋果為什么要廢棄dispatch_get_current_queue亏栈?

?容易誤用造成死鎖

如何用GCD同步若干個異步調(diào)用台腥?(如根據(jù)若干個url異步加載多張圖片,然后在都下載完成后合成一張整圖)

?必須是并發(fā)隊列才起作用

?需求分析

○首先绒北,分別異步執(zhí)行2個耗時的操作

○其次黎侈,等2個異步操作都執(zhí)行完畢后,再回到主線程執(zhí)行一些操作

?使用隊列組實現(xiàn)上面的需求

//創(chuàng)建隊列組

dispatch_group_t

group =? dispatch_group_create();

//獲取全局并發(fā)隊列

dispatch_queue_t

queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//往隊列組中添加耗時操作

dispatch_group_async(group,

queue, ^{

//執(zhí)行耗時的異步操作1

});

//往隊列組中添加耗時操作

dispatch_group_async(group,

queue, ^{

//執(zhí)行耗時的異步操作2

});

//當并發(fā)隊列組中的任務執(zhí)行完畢后才會執(zhí)行這里的代碼

dispatch_group_notify(group,

queue, ^{

//如果這里還有基于上面兩個任務的結果繼續(xù)執(zhí)行一些代碼闷游,建議還是放到子線程中峻汉,等代碼執(zhí)行完畢后在回到主線程

//回到主線程

dispatch_async(group, dispatch_get_main_queue(), ^{

//執(zhí)行相關代碼...

});

});

#dispatch_barrier_async的作用是什么?

?函數(shù)定義

dispatch_barrier_async(dispatch_queue_t

queue, dispatch_block_t block);

?必須是并發(fā)隊列脐往,要是串行隊列俱济,這個函數(shù)就沒啥意義了

?注意:這個函數(shù)的第一個參數(shù)queue不能是全局的并發(fā)隊列

?作用:在它前面的任務執(zhí)行結束后它才執(zhí)行,在它后面的任務等它執(zhí)行完成后才會執(zhí)

?示例代碼

-(void)barrier

{

dispatch_queue_t queue = dispatch_queue_create("12342234",DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

NSLog(@"----1-----%@",[NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----2-----%@",[NSThread currentThread]);

});

//在它前面的任務執(zhí)行結束后它才執(zhí)行钙勃,在它后面的任務等它執(zhí)行完成后才會執(zhí)行

dispatch_barrier_async(queue, ^{

NSLog(@"----barrier-----%@",[NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----3-----%@",[NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----4-----%@",[NSThread currentThread]);

});

}

#以下代碼運行結果如何蛛碌?

- (void)viewDidLoad

{

[superviewDidLoad];

NSLog(@"1");

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog(@"2");

});

NSLog(@"3");

}

?答案:主線程死鎖

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市辖源,隨后出現(xiàn)的幾起案子蔚携,更是在濱河造成了極大的恐慌,老刑警劉巖克饶,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酝蜒,死亡現(xiàn)場離奇詭異,居然都是意外死亡矾湃,警方通過查閱死者的電腦和手機亡脑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霉咨,你說我怎么就攤上這事蛙紫。” “怎么了途戒?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵坑傅,是天一觀的道長。 經(jīng)常有香客問我喷斋,道長唁毒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任星爪,我火速辦了婚禮浆西,結果婚禮上,老公的妹妹穿的比我還像新娘顽腾。我一直安慰自己室谚,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布崔泵。 她就那樣靜靜地躺著秒赤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪憎瘸。 梳的紋絲不亂的頭發(fā)上入篮,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音幌甘,去河邊找鬼潮售。 笑死,一個胖子當著我的面吹牛锅风,可吹牛的內(nèi)容都是我干的酥诽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皱埠,長吁一口氣:“原來是場噩夢啊……” “哼肮帐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起边器,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤训枢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忘巧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恒界,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年砚嘴,在試婚紗的時候發(fā)現(xiàn)自己被綠了十酣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩拙。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耸采,靈堂內(nèi)的尸體忽然破棺而出兴泥,到底是詐尸還是另有隱情,我是刑警寧澤洋幻,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站翅娶,受9級特大地震影響文留,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜竭沫,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一燥翅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜕提,春花似錦森书、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脏榆,卻和暖如春猖毫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背须喂。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工吁断, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坞生。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓仔役,卻偏偏與公主長得像,于是被迫代替她去往敵國和親是己。 傳聞我的和親對象是個殘疾皇子又兵,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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