1. 簡(jiǎn)單介紹下NSURLConnection類(lèi)及+ sendSynchronousRequest:returningResponse:error:與– initWithRequest:delegate:兩個(gè)方法的區(qū)別?
答: NSURLConnection主要用于網(wǎng)絡(luò)訪問(wèn),其中+ sendSynchronousRequest:returningResponse:error:是同步訪問(wèn)數(shù)據(jù),即當(dāng)前線程會(huì)阻塞,并等待request的返回的response,而– initWithRequest:delegate:使用的是異步加載,當(dāng)其完成網(wǎng)絡(luò)訪問(wèn)后,會(huì)通過(guò)delegate回到主線程,并其委托的對(duì)象。
2. 在項(xiàng)目什么時(shí)候選擇使用GCD,什么時(shí)候選擇NSOperation
答: 項(xiàng)目中使用NSOperation的優(yōu)點(diǎn)是NSOperation是對(duì)線程的高度抽象,在項(xiàng)目中使用它,會(huì)使項(xiàng)目的程序結(jié)構(gòu)更好,子類(lèi)化NSOperation的設(shè)計(jì)思路,是具有面向?qū)ο蟮膬?yōu)點(diǎn)(復(fù)用、封裝),使得實(shí)現(xiàn)是多線程支持,而接口簡(jiǎn)單,建議在復(fù)雜項(xiàng)目中使用。
項(xiàng)目中使用GCD的優(yōu)點(diǎn)是GCD本身非常簡(jiǎn)單、易用,對(duì)于不復(fù)雜的多線程操作,會(huì)節(jié)省代碼量,而B(niǎo)lock參數(shù)的使用,會(huì)是代碼更為易讀,建議在簡(jiǎn)單項(xiàng)目中使用。
3. ViewController的didReceiveMemoryWarning怎么被調(diào)用
答:[supper didReceiveMemoryWarning];
4. 寫(xiě)一個(gè)setter方法用于完成@property(nonatomic, retain) NSString *name,寫(xiě)一個(gè)setter方法用于完成@property(nonatomic, copy) NSString *name
- (void)setName:(NSString *)str{
[str retain];
[_name release];
_name = str;
}
- (void)setName:(NSString *)str{
id t = [str copy];
[_name release];
_name = t;
}
5. 對(duì)于語(yǔ)句NSString *obj = [[NSData alloc] init]; obj在編譯時(shí)和運(yùn)行時(shí)分別時(shí)什么類(lèi)型的對(duì)象?
答: 編譯時(shí)是NSString的類(lèi)型;運(yùn)行時(shí)是NSData類(lèi)型的對(duì)象
6. Object C中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么?
答:線程創(chuàng)建有三種方法:使用NSThread創(chuàng)建、使用GCD的dispatch、使用子類(lèi)化的NSOperation,然后將其加入NSOperationQueue;在主線程執(zhí)行代碼,方法是performSelectorOnMainThread,如果想延時(shí)執(zhí)行代碼可以用performSelector:onThread:withObject:waitUntilDone:
7. 淺復(fù)制和深復(fù)制的區(qū)別?
答:淺層復(fù)制:只復(fù)制指向?qū)ο蟮闹羔?,而不?fù)制引用對(duì)象本身。
深層復(fù)制:復(fù)制引用對(duì)象本身。
8. PerformSelecter
當(dāng)調(diào)用 NSObject 的
performSelecter:afterDelay:后,實(shí)際上其內(nèi)部會(huì)創(chuàng)建一個(gè) Timer 并添加到當(dāng)前線程的 RunLoop 中。所以如果當(dāng)前線程沒(méi)有 RunLoop,則這個(gè)方法會(huì)失效。
當(dāng)調(diào)用performSelector:onThread:時(shí),實(shí)際上其會(huì)創(chuàng)建一個(gè) Timer 加到對(duì)應(yīng)的線程去,同樣的,如果對(duì)應(yīng)線程沒(méi)有 RunLoop 該方法也會(huì)失效。
9. 優(yōu)化你是從哪幾方面著手?
一、首頁(yè)啟動(dòng)速度
啟動(dòng)過(guò)程中做的事情越少越好(盡可能將多個(gè)接口合并)
不在UI線程上作耗時(shí)的操作(數(shù)據(jù)的處理在子線程進(jìn)行,處理完通知主線程刷新節(jié)目)
在合適的時(shí)機(jī)開(kāi)始后臺(tái)任務(wù)(例如在用戶(hù)指引節(jié)目就可以開(kāi)始準(zhǔn)備加載的數(shù)據(jù))
二、頁(yè)面瀏覽速度
json的處理(iOS 自帶的NSJSONSerialization,Jsonkit,SBJson)
數(shù)據(jù)的分頁(yè)(后端數(shù)據(jù)多的話,就要分頁(yè)返回,例如網(wǎng)易新聞,或者 微博記錄)
數(shù)據(jù)壓縮(大數(shù)據(jù)也可以壓縮返回,減少流量,加快反應(yīng)速度)
內(nèi)容緩存(例如網(wǎng)易新聞的最新新聞列表都是要緩存到本地,從本地加載,可以緩存到內(nèi)存,或者數(shù)據(jù)庫(kù),根據(jù)情況而定)
延時(shí)加載tab(比如app有5個(gè)tab,可以先加載第一個(gè)要顯示的tab,其他的在顯示時(shí)候加載,按需加載)
算法的優(yōu)化(核心算法的優(yōu)化,例如有些app 有個(gè) 聯(lián)系人姓名用漢語(yǔ)拼音的首字母排序)
三、操作流暢度優(yōu)化
Tableview 優(yōu)化(tableview cell的加載優(yōu)化)
ViewController加載優(yōu)化(不同view之間的跳轉(zhuǎn),可以提前準(zhǔn)備好數(shù)據(jù))
四、數(shù)據(jù)庫(kù)的優(yōu)化
數(shù)據(jù)庫(kù)設(shè)計(jì)上面的重構(gòu)
查詢(xún)語(yǔ)句的優(yōu)化
分庫(kù)分表(數(shù)據(jù)太多的時(shí)候,可以分不同的表或者庫(kù))
五、服務(wù)器端和客戶(hù)端的交互優(yōu)化
客戶(hù)端盡量減少請(qǐng)求
服務(wù)端盡量做多的邏輯處理
服務(wù)器端和客戶(hù)端采取推拉結(jié)合的方式(可以利用一些同步機(jī)制)
通信協(xié)議的優(yōu)化(減少報(bào)文的大?。?br> 電量使用優(yōu)化(盡量不要使用后臺(tái)運(yùn)行)
六、非技術(shù)性能優(yōu)化
產(chǎn)品設(shè)計(jì)的邏輯性(產(chǎn)品的設(shè)計(jì)一定要符合邏輯,或者邏輯盡量簡(jiǎn)單,否則會(huì)讓程序員抓狂,有時(shí)候用了好大力氣,才可以完成一個(gè)小小的邏輯設(shè)計(jì)問(wèn)題)
界面交互的規(guī)范(每個(gè)模塊的界面的交互盡量統(tǒng)一,符合操作習(xí)慣)
代碼規(guī)范(這個(gè)可以隱形帶來(lái)app 性能的提高,比如 用if else 還是switch ,或者是用!還是 ==)
code review(堅(jiān)持code Review 持續(xù)重構(gòu)代碼。減少代碼的邏輯復(fù)雜度)
10. 什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?
1.在ARC中,在有可能出現(xiàn)循環(huán)引用的時(shí)候,往往要通過(guò)讓其中一端使用 weak 來(lái)解決,比如: delegate 代理屬性。
2.自身已經(jīng)對(duì)它進(jìn)行一次強(qiáng)引用,沒(méi)有必要再?gòu)?qiáng)引用一次,此時(shí)也會(huì)使用 weak,如自定義 IBOutlet 控件屬性一般也使用 weak;當(dāng)然,也可以使用strong。
IBOutlet連出來(lái)的視圖屬性為什么可以被設(shè)置成weak?
答:因?yàn)楦缚丶膕ubViews數(shù)組已經(jīng)對(duì)它有一個(gè)強(qiáng)引用。
不同點(diǎn)
assign 可以用非 OC 對(duì)象,而 weak 必須用于 OC 對(duì)象。
weak 表明該屬性定義了一種“非擁有關(guān)系”。在屬性所指的對(duì)象銷(xiāo)毀時(shí),屬性值會(huì)自動(dòng)清空(nil)。
11. 用@property聲明的 NSString / NSArray / NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,為什么?如果改用strong關(guān)鍵字,可能造成什么問(wèn)題?
答:用 @property 聲明 NSString、NSArray、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,是因?yàn)樗麄冇袑?duì)應(yīng)的可變類(lèi)型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進(jìn)行賦值操作(就是把可變的賦值給不可變的),為確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng),應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
1.因?yàn)楦割?lèi)指針可以指向子類(lèi)對(duì)象,使用 copy 的目的是為了讓本對(duì)象的屬性不受外界影響,使用 copy 無(wú)論給我傳入是一個(gè)可變對(duì)象還是不可對(duì)象,我本身持有的就是一個(gè)不可變的副本。
2.如果我們使用是 strong ,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性。
總結(jié):使用copy的目的是,防止把可變類(lèi)型的對(duì)象賦值給不可變類(lèi)型的對(duì)象時(shí),可變類(lèi)型對(duì)象的值發(fā)送變化會(huì)無(wú)意間篡改不可變類(lèi)型對(duì)象原來(lái)的值。
12. runtime如何實(shí)現(xiàn)weak變量的自動(dòng)置nil?
runtime對(duì)注冊(cè)的類(lèi),會(huì)進(jìn)行布局,會(huì)將 weak 對(duì)象放入一個(gè) hash 表中。用 weak 指向的對(duì)象內(nèi)存地址作為 key,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì)調(diào)用對(duì)象的 dealloc 方法,假設(shè) weak 指向的對(duì)象內(nèi)存地址是a,那么就會(huì)以a為key,在這個(gè) weak hash表中搜索,找到所有以a為key的 weak 對(duì)象,從而設(shè)置為nil。
13. runloop是什么/runloop的概念?
runloop是線程相關(guān)的基礎(chǔ)框架的一部分。一個(gè)runloop就是一個(gè)事件處理的循環(huán),用來(lái)不停的調(diào)度工作以及處理輸入事件。其實(shí)內(nèi)部就是do-while循環(huán),這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)(比如Source,Timer,Observer)。使用runloop的目的是讓你的線程在有工作的時(shí)候忙于工作,而沒(méi)工作的時(shí)候處于休眠狀態(tài)。
這是一個(gè)我的iOS交流群點(diǎn)擊進(jìn)群:761407670 進(jìn)群密碼123,群內(nèi)有很多最近在面試的小伙伴,歡迎大家進(jìn)來(lái)一起交流面試經(jīng)驗(yàn),交流技術(shù)。
另外附帶一份群內(nèi)收集的面試題,進(jìn)群可自行下載!
14. UITableViewCell上有個(gè)UILabel,顯示NSTimer實(shí)現(xiàn)的秒表時(shí)間,手指滾動(dòng)cell過(guò)程中,label是否刷新,為什么?
這是否刷新取決于timer加入到Run Loop中的Mode是什么。Mode主要是用來(lái)指定事件在運(yùn)行循環(huán)中的優(yōu)先級(jí)的,分為
- NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn),空閑狀態(tài)
- UITrackingRunLoopMode:ScrollView滑動(dòng)時(shí)會(huì)切換到該Mode
- UIInitializationRunLoopMode:run loop啟動(dòng)時(shí),會(huì)切換到該mode
- NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
蘋(píng)果公開(kāi)提供的Mode有兩個(gè)- NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
- NSRunLoopCommonModes(kCFRunLoopCommonModes)
在編程中:如果我們把一個(gè)NSTimer對(duì)象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運(yùn)行循環(huán)中的時(shí)候, ScrollView滾動(dòng)過(guò)程中會(huì)因?yàn)閙ode的切換,而導(dǎo)致NSTimer將不再被調(diào)度。當(dāng)我們滾動(dòng)的時(shí)候,也希望不調(diào)度,那就應(yīng)該使用默認(rèn)模式。但是,如果希望在滾動(dòng)時(shí),定時(shí)器也要回調(diào),那就應(yīng)該使用common mode。
15. NStimer準(zhǔn)嗎?談?wù)勀愕目捶??如果不?zhǔn)該怎樣實(shí)現(xiàn)一個(gè)精確的NSTimer?
不準(zhǔn);不準(zhǔn)的原因如下
1、NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main負(fù)責(zé)所有主線程事件,例如UI界面的操作,復(fù)雜的運(yùn)算,這樣在同一個(gè)runloop中timer就會(huì)產(chǎn)生阻塞。
2、模式的改變。主線程的 RunLoop 里有兩個(gè)預(yù)置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。
當(dāng)你創(chuàng)建一個(gè) Timer 并加到 DefaultMode 時(shí),Timer 會(huì)得到重復(fù)回調(diào),但此時(shí)滑動(dòng)一個(gè)ScrollView時(shí),RunLoop 會(huì)將 mode 切換為 TrackingRunLoopMode,這時(shí) Timer 就不會(huì)被回調(diào),并且也不會(huì)影響到滑動(dòng)操作。所以就會(huì)影響到NSTimer不準(zhǔn)的情況。
PS:DefaultMode 是 App 平時(shí)所處的狀態(tài),rackingRunLoopMode 是追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)。
方法:
1、在主線程中進(jìn)行NSTimer操作,但是將NSTimer實(shí)例加到main runloop的特定mode(模式)中。避免被復(fù)雜運(yùn)算操作或者UI界面刷新所干擾self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
2、在子線程中進(jìn)行NSTimer的操作,再在主線程中修改UI界面顯示操作結(jié)果
-(void)timerMethod2 {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}
-(void)newThread{
@autoreleasepool{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
}
16. NSOperation 相比于 GCD 有哪些優(yōu)勢(shì)?
GCD是基于c的底層api,NSOperation屬于object-c類(lèi)。ios 首先引入的是NSOperation,IOS4之后引入了GCD和NSOperationQueue并且其內(nèi)部是用gcd實(shí)現(xiàn)的。
相對(duì)于GCD:
1、NSOperation擁有更多的函數(shù)可用,具體查看api。
2、在NSOperationQueue中,可以建立各個(gè)NSOperation之間的依賴(lài)關(guān)系。
3、有kvo可以監(jiān)測(cè)operation是否正在執(zhí)行(isExecuted)、是否結(jié)束(isFinished),是否取消(isCanceld)。
4、NSOperationQueue可以方便的管理并發(fā)、NSOperation之間的優(yōu)先級(jí)。
GCD主要與block結(jié)合使用。代碼簡(jiǎn)潔高效。
GCD也可以實(shí)現(xiàn)復(fù)雜的多線程應(yīng)用,主要是建立個(gè)個(gè)線程時(shí)間的依賴(lài)關(guān)系這類(lèi)的情況,但是需要自己實(shí)現(xiàn)相比NSOperation要復(fù)雜。
具體使用哪個(gè),依需求而定。 從個(gè)人使用的感覺(jué)來(lái)看,比較合適的用法是:除了依賴(lài)關(guān)系盡量使用GCD,因?yàn)樘O(píng)果專(zhuān)門(mén)為GCD做了性能上面的優(yōu)化。
17. 如何訪問(wèn)并修改一個(gè)類(lèi)的私有屬性?
有兩種方法可以訪問(wèn)私有屬性,一種是通過(guò)KVC獲取,一種是通過(guò)runtime訪問(wèn)并修改私有屬性。
18. 如何捕獲異常?
1. 在app啟動(dòng)時(shí)(didFinishLaunchingWithOptions),添加一個(gè)異常捕獲的監(jiān)聽(tīng)
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
2. 實(shí)現(xiàn)捕獲異常日志并保存到本地的方法
void UncaughtExceptionHandler(NSException *exception){
//異常日志獲取
NSArray *excpArr = [exception callStackSymbols];
NSString *reason = [exception reason];
NSString *name = [exception name];
NSString *excpCnt = [NSString stringWithFormat:@"exceptionType: %@ \n reason: %@ \n stackSymbols: %@",name,reason,excpArr];
//日常日志保存(可以將此功能單獨(dú)提煉到一個(gè)方法中)
NSArray *dirArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dirPath = dirArr[0];
NSString *logDir = [dirPath stringByAppendingString:@"/CrashLog"];
BOOL isExistLogDir = YES;
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:logDir]) {
isExistLogDir = [fileManager createDirectoryAtPath:logDir withIntermediateDirectories:YES attributes:nil error:nil];
}
if (isExistLogDir) {
//此處可擴(kuò)展
NSString *logPath = [logDir stringByAppendingString:@"/crashLog.txt"];
[excpCnt writeToFile:logPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
}
19. Object-c的類(lèi)可以多重繼承么?可以實(shí)現(xiàn)多個(gè)接口么?Category是什么?重寫(xiě)一個(gè)類(lèi)的方式用繼承好還是分類(lèi)好?為什么?
答:Object-c的類(lèi)不可以多重繼承;可以實(shí)現(xiàn)多個(gè)接口,通過(guò)實(shí)現(xiàn)多個(gè)接口可以完成C++的多重繼承;Category是類(lèi)別,一般情況用分類(lèi)好,用Category去重寫(xiě)類(lèi)的方法,僅對(duì)本Category有效,不會(huì)影響到其他類(lèi)與原有類(lèi)的關(guān)系。
20. Category(分類(lèi)),Extension(擴(kuò)展)和繼承的區(qū)別
答:1.分類(lèi)
category原則上只能在現(xiàn)有類(lèi)基礎(chǔ)上添加新的方法(能添加屬性的原因只是通過(guò)runtime解決無(wú)setter/getter的問(wèn)題而已),類(lèi)別中的方法沒(méi)被實(shí)現(xiàn)編譯器是不會(huì)有任何警告的,這是因?yàn)轭?lèi)別是在運(yùn)行時(shí)添加到類(lèi)中的
2.擴(kuò)展
iOS中的extension就是匿名的分類(lèi),只有頭文件沒(méi)有實(shí)現(xiàn)文件。類(lèi)擴(kuò)展不僅可以增加方法,還可以增加實(shí)例變量(或者屬性),只是該實(shí)例變量默認(rèn)是@private類(lèi)型的(使用范圍只能在自身類(lèi),而不是子類(lèi)或其他地方),類(lèi)擴(kuò)展中聲明的方法沒(méi)被實(shí)現(xiàn),編譯器會(huì)報(bào)警,這是因?yàn)轭?lèi)擴(kuò)展是在編譯階段被添加到類(lèi)中的
3.繼承
在iOS中繼承是單繼承,既只能有一個(gè)父類(lèi)。在繼承中,子類(lèi)可以使用父類(lèi)的方法和變量,當(dāng)子類(lèi)想對(duì)本類(lèi)或者父類(lèi)的變量進(jìn)行初始化,那么需要重寫(xiě)init()方法 。父類(lèi)也可以訪問(wèn)子類(lèi)的方法和成員變量
21. 簡(jiǎn)述內(nèi)存分區(qū)情況
1).代碼區(qū):存放函數(shù)二進(jìn)制代碼
2).數(shù)據(jù)區(qū):系統(tǒng)運(yùn)行時(shí)申請(qǐng)內(nèi)存并初始化,系統(tǒng)退出時(shí)由系統(tǒng)釋放。存放全局變量、靜態(tài)變量、常量
3).堆區(qū):通過(guò)malloc等函數(shù)或new等操作符動(dòng)態(tài)申請(qǐng)得到,需程序員手動(dòng)申請(qǐng)和釋放
4).棧區(qū):函數(shù)模塊內(nèi)申請(qǐng),函數(shù)結(jié)束時(shí)由系統(tǒng)自動(dòng)釋放。存放局部變量、函數(shù)參數(shù)
22. 直接調(diào)用_objc_msgForward函數(shù)將會(huì)發(fā)生什么?
_objc_msgForward是 IMP 類(lèi)型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息,但它并沒(méi)有實(shí)現(xiàn)的時(shí)候,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)。
直接調(diào)用_objc_msgForward是非常危險(xiǎn)的事,如果用不好會(huì)直接導(dǎo)致程序Crash,但是如果用得好,能做很多非??岬氖隆?br> 一旦調(diào)用_objc_msgForward,將跳過(guò)查找 IMP 的過(guò)程,直接觸發(fā)“消息轉(zhuǎn)發(fā)”,如果調(diào)用了_objc_msgForward,即使這個(gè)對(duì)象確實(shí)已經(jīng)實(shí)現(xiàn)了這個(gè)方法,你也會(huì)告訴objc_msgSend:“我沒(méi)有在這個(gè)對(duì)象里找到這個(gè)方法的實(shí)現(xiàn)”
23. 對(duì)于Run Loop的理解
- RunLoop,是多線程的法寶,即一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完任務(wù)后就會(huì)退出線程。主線程執(zhí)行完即時(shí)任務(wù)時(shí)會(huì)繼續(xù)等待接收事件而不退出。非主線程通常來(lái)說(shuō)就是為了執(zhí)行某一任務(wù)的,執(zhí)行完畢就需要?dú)w還資源,因此默認(rèn)是不運(yùn)行RunLoop的;
- 每一個(gè)線程都有其對(duì)應(yīng)的RunLoop,只是默認(rèn)只有主線程的RunLoop是啟動(dòng)的,其它子線程的RunLoop默認(rèn)是不啟動(dòng)的,若要啟動(dòng)則需要手動(dòng)啟動(dòng);
- 在一個(gè)單獨(dú)的線程中,如果需要在處理完某個(gè)任務(wù)后不退出,繼續(xù)等待接收事件,則需要啟用RunLoop;
- NSRunLoop提供了一個(gè)添加NSTimer的方法,可以指定Mode,如果要讓任何情況下都回調(diào),則需要設(shè)置Mode為Common模式;
- 實(shí)質(zhì)上,對(duì)于子線程的runloop默認(rèn)是不存在的,因?yàn)樘O(píng)果采用了懶加載的方式。如果我們沒(méi)有手動(dòng)調(diào)用[NSRunLoop currentRunLoop]的話,就不會(huì)去查詢(xún)是否存在當(dāng)前線程的RunLoop,也就不會(huì)去加載,更不會(huì)創(chuàng)建。
24. runtime如何通過(guò)selector找到對(duì)應(yīng)的IMP地址?(分別考慮類(lèi)方法和實(shí)例方法)
1.每一個(gè)類(lèi)對(duì)象中都一個(gè)對(duì)象方法列表(對(duì)象方法緩存)
2.類(lèi)方法列表是存放在類(lèi)對(duì)象中isa指針指向的元類(lèi)對(duì)象中(類(lèi)方法緩存)
3.方法列表中每個(gè)方法結(jié)構(gòu)體中記錄著方法的名稱(chēng),方法實(shí)現(xiàn),以及參數(shù)類(lèi)型,其實(shí)selector本質(zhì)就是方法名稱(chēng),通過(guò)這個(gè)方法名稱(chēng)就可以在方法列表中找到對(duì)應(yīng)的方法實(shí)現(xiàn).
4.當(dāng)我們發(fā)送一個(gè)消息給一個(gè)NSObject對(duì)象時(shí),這條消息會(huì)在對(duì)象的類(lèi)對(duì)象方法列表里查找
5.當(dāng)我們發(fā)送一個(gè)消息給一個(gè)類(lèi)時(shí),這條消息會(huì)在類(lèi)的Meta Class對(duì)象的方法列表里查找
25. runtime 中,SEL 和 IMP 的區(qū)別
方法名 SEL – 表示該方法的名稱(chēng);
IMP – 指向該方法的具體實(shí)現(xiàn)的函數(shù)指針,說(shuō)白了IMP就是實(shí)現(xiàn)方法。
26.block底層實(shí)現(xiàn)
block本質(zhì)是指向一個(gè)結(jié)構(gòu)體的一個(gè)指針
運(yùn)行時(shí)機(jī)制 比較高級(jí)的特性 純C語(yǔ)言
平時(shí)寫(xiě)的OC代碼 轉(zhuǎn)換成C語(yǔ)言運(yùn)行時(shí)的代碼
指令:clang -rewrite-objc main.m(可以打印驗(yàn)證)
默認(rèn)情況下,任何block都是在棧里面的,隨時(shí)可能被回收
只要對(duì)其做一次copy操作 block的內(nèi)存就會(huì)放在堆里面 不會(huì)釋放
只有copy才能產(chǎn)生一個(gè)新的內(nèi)存地址 所有地址會(huì)發(fā)生改變
27. TCP協(xié)議三次握手
TCP協(xié)議采用了三次握手策略。用TCP協(xié)議把數(shù)據(jù)包送出去后,TCP不會(huì)對(duì)傳送后的情況置之不理,它一定會(huì)向?qū)Ψ酱_認(rèn)是否成功送達(dá)。握手過(guò)程中使用了TCP的標(biāo)志——SYN(synchronize)和ACK(acknowledgement)。發(fā)送端首先發(fā)送一個(gè)帶SYN標(biāo)志的數(shù)據(jù)包給對(duì)方。接收端收到后,回傳一個(gè)帶有SYN/ACK標(biāo)志的數(shù)據(jù)包以示傳達(dá)確認(rèn)信息。最后,發(fā)送端再回傳一個(gè)帶ACK標(biāo)志的數(shù)據(jù)包,代表“握手”結(jié)束。
TCP協(xié)議三次握手示意圖
28. @property 的本質(zhì)是什么?
@property = ivar + getter + setter;
“屬性” (property)有兩大概念:ivar(實(shí)例變量)、getter+setter(存取方法)
29. KVC的底層實(shí)現(xiàn)?
當(dāng)一個(gè)對(duì)象調(diào)用setValue方法時(shí),方法內(nèi)部會(huì)做以下操作:
1). 檢查是否存在相應(yīng)的key的set方法,如果存在,就調(diào)用set方法。
2). 如果set方法不存在,就會(huì)查找與key相同名稱(chēng)并且?guī)聞澗€的成員變量,如果有,則直接給成員變量屬性賦值。
3). 如果沒(méi)有找到_key,就會(huì)查找相同名稱(chēng)的屬性key,如果有就直接賦值。
4). 如果還沒(méi)有找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
這些方法的默認(rèn)實(shí)現(xiàn)都是拋出異常,我們可以根據(jù)需要重寫(xiě)它們。
30. ViewController生命周期
按照?qǐng)?zhí)行順序排列:
1). initWithCoder:通過(guò)nib文件初始化時(shí)觸發(fā)。
2). awakeFromNib:nib文件被加載的時(shí)候,會(huì)發(fā)生一個(gè)awakeFromNib的消息到nib文件中的每個(gè)對(duì)象。
3). loadView:開(kāi)始加載視圖控制器自帶的view。
4). viewDidLoad:視圖控制器的view被加載完成。
5). viewWillAppear:視圖控制器的view將要顯示在window上。
6). updateViewConstraints:視圖控制器的view開(kāi)始更新AutoLayout約束。
7). viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置。
8). viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置。
9). viewDidAppear:視圖控制器的view已經(jīng)展示到window上。
10). viewWillDisappear:視圖控制器的view將要從window上消失。
11). viewDidDisappear:視圖控制器的view已經(jīng)從window上消失。
31. 如何用GCD同步若干個(gè)異步調(diào)用?(如根據(jù)若干個(gè)url異步加載多張圖片,然后在都下載完成后合成一張整圖)
// 使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢,就會(huì)執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ });
// 當(dāng)并發(fā)隊(duì)列組中的任務(wù)執(zhí)行完畢后才會(huì)執(zhí)行這里的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并圖片
});
32. dispatch_barrier_async(柵欄函數(shù))的作用是什么?
函數(shù)定義:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
作用:
1.在它前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,它后面的任務(wù)要等它執(zhí)行完成后才會(huì)開(kāi)始執(zhí)行。
2.避免數(shù)據(jù)競(jìng)爭(zhēng)
// 1.創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向隊(duì)列中添加任務(wù)
dispatch_async(queue, ^{ // 1.2是并行的
NSLog(@"任務(wù)1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2, %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"任務(wù) barrier, %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{ // 這兩個(gè)是同時(shí)執(zhí)行的
NSLog(@"任務(wù)3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4, %@",[NSThread currentThread]);
});
// 輸出結(jié)果: 任務(wù)1 任務(wù)2 ——》 任務(wù) barrier ——》任務(wù)3 任務(wù)4
// 其中的任務(wù)1與任務(wù)2,任務(wù)3與任務(wù)4 由于是并行處理先后順序不定。

