經典案例
===CGD考察===
下面代碼的輸出順序
- (void)gcdTest {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"4");
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"5");
});
[self performSelector:@selector(test2)];
[self performSelector:@selector(test3) withObject:nil afterDelay:0];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"6");
});
[self test1];
}
- (void)test1 {
NSLog(@"1");
}
- (void)test2 {
NSLog(@"2");
}
- (void)test3 {
NSLog(@"3");
}
/*
蘋果文檔
Enqueue a block for execution at the specified time.
This function waits until the specified time and then asynchronously adds block to the specified queue.
dispatch_after 第二個參數為0别智,以此可以理解為在當前時刻往主隊列添加一個block, 文檔中 asynchronously 可以看出是在當前時刻
添加到主隊列的尾部
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"4");
});
/*
這里主要考的是 dispatch_async 這個函數的特點照藻,往指定的隊列添加一個block立馬返回
以下代碼在主線程上執(zhí)行
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"A");
});
NSLog(@"B");
結果為 BA
*/
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"5");
});
/*
performSelector:
先看蘋果文檔的解析:
Sends a specified message to the receiver and returns the result of the message.
那么這句話相當于
[self test2]
*/
[self performSelector:@selector(test2)];
/*
performSelector:withObject:afterDelay:
Invokes a method of the receiver on the current thread using the default mode after a delay.
This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode.
這些解析中最關鍵的一個單詞是 NSDefaultRunLoopMode
NSDefaultRunLoopMode 是當前線程上正在執(zhí)行的任務優(yōu)先,
那么如果當前線程上有其他任務要執(zhí)行魔策,test3 先讓步氓皱,然后在輪到它執(zhí)行
如果往當前隊列中 dispatch_async 一下,通過測試续室,他的優(yōu)先級跟 performSelector:withObject:afterDelay: 是同等的栋烤,
換句話說就是誰先誰先執(zhí)行
*/
[self performSelector:@selector(test3) withObject:nil afterDelay:0];
/*
這里主要開啟了一個異步線程,再加上 使用了 dispatch_async 函數 因此
NSLog(@"6"); 在此打印的比較隨機
*/
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"6");
});
[self test1];
/*
綜上所述:
執(zhí)行的順序為
test2
test1
NSLog(@"4");
NSLog(@"5");
test3
NSLog(@"6"); 的出現隨機
*/
下面代碼會造成什么問題挺狰,解釋原因
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(@"=================4");
dispatch_sync(dispatch_get_main_queue(),
^{ NSLog(@"=================5"); });
NSLog(@"=================6");
}
分析上面代碼:
viewDidLoad 在主線程中明郭, 及在dispatch_get_main_queue() 中,
執(zhí)行到sync 時 向dispatch_get_main_queue()插入 同步 threed丰泊。
sync 會等到 后面block 執(zhí)行完成才返回薯定, sync 又再 dispatch_get_main_queue() 隊列中,
它是串行隊列瞳购,sync 是后加入的话侄,
前一個是主線程,所以 sync 想執(zhí)行 block 必須等待主線程執(zhí)行完成学赛,
主線程等待 sync 返回年堆,去執(zhí)行后續(xù)內容。
照成死鎖盏浇,sync 等待mainThread 執(zhí)行完成变丧, mianThread 等待sync 函數返回。
下面例子:
- (void)viewDidLoad{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"=================1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================2"); });
NSLog(@"=================3"); });
}
程序會完成執(zhí)行缠捌,為什么不會出現死鎖锄贷。
首先: async 在主線程中 創(chuàng)建了一個異步線程 加入 全局并發(fā)隊列,
async 不會等待block 執(zhí)行完成曼月,立即返回谊却,
1,async 立即返回哑芹, viewDidLoad 執(zhí)行完畢炎辨,及主線程執(zhí)行完畢。
2聪姿,同時碴萧,全局并發(fā)隊列立即執(zhí)行異步 block , 打印 1末购,
當執(zhí)行到 sync 它會等待 block 執(zhí)行完成才返回破喻,
及等待dispatch_get_main_queue() 隊列中的 mianThread 執(zhí)行完成, 然后才開始調用block 盟榴。
===Runloop===
runloop是什么曹质?在項目中有具體的應用嗎?好像沒什么可以用到的地方啊羽德?
Runloop几莽,運行循環(huán)。當我們應用啟動之后宅静,就會在主線程開啟一個運行循環(huán)章蚣,來監(jiān)聽我們的觸摸事件和消息,等姨夹。注意纤垂,這個時候,子線程中是默認不開啟運行循環(huán)的匀伏,另外洒忧,也不建議開啟蝴韭。
在項目中應用的話够颠,
1. RunLoop保證子線程的長時間存活,而不是執(zhí)行完任務后就立刻銷毀的應用場景榄鉴。這個是系統(tǒng)自己在底層自己處理得履磨。
2. 滑動與圖片刷新: 當tableview的cell上有需要從網絡獲取的圖片的時候,滾動tableView庆尘,異步線程會去加載圖片剃诅,加載完成后主線程就會設置cell的圖片,但是會造成卡頓驶忌∶可以讓設置圖片的任務在CFRunLoopDefaultMode下進行,當滾動tableView的時候付魔,RunLoop是在 UITrackingRunLoopMode 下進行聊品,不去設置圖片,而是當停止的時候几苍,再去設置圖片翻屈。
- (void)viewDidLoad {
[super viewDidLoad];
// 只在NSDefaultRunLoopMode下執(zhí)行(刷新圖片)
[self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];
}
3. 保持子線程一直處理事件: 為了保證線程長期運轉,可以在子線程中加入RunLoop妻坝,并且給Runloop設置item伸眶,防止Runloop自動退出。
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
===RunTime===
你會遇到的Runtime面試題
Objective-C 是面相運行時的語言(runtime oriented language)刽宪,就是說它會盡可能的把編譯和鏈接時要執(zhí)行的邏輯延遲到運行時厘贼。這就給了你很大的靈活性,你可以按需要把消息重定向給合適的對象圣拄,你甚 至可以交換方法的實現嘴秸,等等。
RunTime簡稱運行時。就是系統(tǒng)在運行的時候的一些機制赁遗,其中最主要的是消息機制署辉。OC的函數調用成為消息發(fā)送。屬于動態(tài)調用過程岩四。在編譯的時候并不能決定真正調用哪個函數(事實證明哭尝,在編 譯階段,OC可以調用任何函數剖煌,即使這個函數并未實現材鹦,只要申明過就不會報錯。而C語言在編譯階段就會報錯)耕姊。只有在真正運行的時候才會根據函數的名稱找 到對應的函數來調用桶唐。
以下面的代碼為例:
[obj makeText];
其中obj是一個對象,makeText是一個函數名稱茉兰。對于這樣一個簡單的調用尤泽。在編譯時RunTime會將上述代碼轉化成
objc_msgSend(obj,@selector(makeText));
首先,編譯器將代碼[obj makeText];轉化為objc_msgSend(obj, @selector (makeText));规脸,在objc_msgSend函數中坯约。首先通過obj的isa指針找到obj對應的class。在Class中先去cache中 通過SEL查找對應函數method(猜測cache中method列表是以SEL為key通過hash表來存儲的莫鸭,這樣能提高函數查找速度)闹丐,若 cache中未找到。再去methodList中查找被因,若methodlist中未找到卿拴,則取superClass中查找。若能找到梨与,則將method加 入到cache中堕花,以方便下次查找,并通過method中的函數指針跳轉到對應的函數中去執(zhí)行蛋欣。
Objective-C Runtime 是什么航徙?
Objective-C 的 Runtime 是一個運行時庫(Runtime Library),它是一個主要使用 C 和匯編寫的庫陷虎,為 C 添加了面相對象的能力并創(chuàng)造了 Objective-C到踏。這就是說它在類信息(Class information) 中被加載,完成所有的方法分發(fā)尚猿,方法轉發(fā)窝稿,等等。Objective-C runtime 創(chuàng)建了所有需要的結構體凿掂,讓 Objective-C 的面相對象編程變?yōu)榭赡堋?/p>
Method Swizzling 原理
在Objective-C中調用一個方法伴榔,其實是向一個對象發(fā)送消息纹蝴,查找消息的唯一依據是selector的名字。利用Objective-C的動態(tài)特性踪少,可以實現在運行時偷換selector對應的方法實現塘安,達到給方法掛鉤的目的。每個類都有一個方法列表援奢,存放著selector的名字和方法實現的映射關系兼犯。IMP有點類似函數指針,指向具體的Method實現集漾。
我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP切黔,
我們可以利用 class_replaceMethod 來修改類,
我們可以利用 method_setImplementation 來直接設置某個方法的IMP具篇,……
歸根結底纬霞,都是偷換了selector的IMP。
基礎核心
1驱显、Objective-C對象模型及應用
2诗芜、 談Objective-C block的實現
冷門問題 沒答案自己解決
1、請解釋下method swizzling秒紧,并說出你一般什么時候會用到它绢陌?
2挨下、當一個空指針(nil pointer)調用了一個方法會發(fā)生什么熔恢?
3、為什么retainCount絕對不能用在發(fā)布的代碼中臭笆?請給出兩個相對獨立的解釋叙淌。
3、請說明一下你查找或者解決內存泄露的處理過程愁铺。這個可以深入了解面試者對內存管理方面的知識鹰霍,instruments的運用及其調試的處理過程。
4茵乱、解釋下自動回收池(autorelease pool)在程序運行時是如何運作的茂洒。
5、當處理屬性申明的時候瓶竭,原子(atomic)跟 非原子(non-atomic)屬性有什么區(qū)別督勺?
6、在C語言中斤贰,你如何能用盡可能短的時間來倒轉一個字符串智哀?
7、遍歷一個NSArray和一個NSSet荧恍,哪一個更快瓷叫?
8、解釋代碼簽名(code signing)是如何運作的。
9摹菠、Objective-C中的posing指的是什么盒卸?
10、copy跟retain有什么區(qū)別次氨?
11世落、frames跟bounds有哪些區(qū)別?
12糟需、執(zhí)行如下的代碼會發(fā)生什么情況屉佳?
Ball *ball = [[[[Ball alloc] init] autorelease] autorelease];
趣味問答
最近有沒有開發(fā)什么好玩的東西?
你最引以為豪的作品是什么洲押?
談一談你常用的開發(fā)工具都有哪些優(yōu)勢武花?
你最敬佩的獨立Mac或者iOS應用開發(fā)者是誰?
最喜歡什么項目杈帐?哪種類型的体箕?
你覺得Xcode有哪些需要改進的地方?
iOS上你最喜歡哪些API挑童?
是否有最中意的錯誤報告累铅?
你最愛以哪種方式來檢驗一項新技術是否好用?