iOS 面試題彙總

1. 簡單介紹下NSURLConnection類及+ sendSynchronousRequest:returningResponse:error:– initWithRequest:delegate:兩個方法的區別?ios

答: NSURLConnection主要用於網絡訪問,其中+ sendSynchronousRequest:returningResponse:error:是同步訪問數據,即當前線程會阻塞,並等待request的返回的response,而– initWithRequest:delegate:使用的是異步加載,當其完成網絡訪問後,會經過delegate回到主線程,並其委託的對象。

2. 在項目何時選擇使用GCD,何時選擇NSOperation程序員

答: 項目中使用NSOperation的優勢是NSOperation是對線程的高度抽象,在項目中使用它,會使項目的程序結構更好,子類化NSOperation的設計思路,是具備面向對象的優勢(複用、封裝),使得實現是多線程支持,而接口簡單,建議在複雜項目中使用。
項目中使用GCD的優勢是GCD自己很是簡單、易用,對於不復雜的多線程操做,會節省代碼量,而Block參數的使用,會是代碼更爲易讀,建議在簡單項目中使用。

3. ViewController的didReceiveMemoryWarning怎麼被調用面試

答:[supper didReceiveMemoryWarning];

4. 寫一個setter方法用於完成@property(nonatomic, retain) NSString *name,寫一個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. 對於語句NSString *obj = [[NSData alloc] init]; obj在編譯時和運行時分別時什麼類型的對象?數據庫

答: 編譯時是NSString的類型;運行時是NSData類型的對象

做爲一個ios開發者,遇到問題的時候,有一個學習的氛圍跟一個交流圈子特別重要對自身有很大幫助,衆人拾柴火焰高 這是一個個人iOS交流羣:711315161,分享BAT,阿里面試題、面試經驗,討論技術, 你們一塊兒交流學習成長!但願幫助開發者少走彎路。編程

6. Object C中建立線程的方法是什麼?若是在主線程中執行代碼,方法是什麼?若是想延時執行代碼、方法又是什麼?json

答:線程建立有三種方法:使用NSThread建立、使用GCD的dispatch、使用子類化的NSOperation,而後將其加入NSOperationQueue;在主線程執行代碼,方法是performSelectorOnMainThread,若是想延時執行代碼能夠用performSelector:onThread:withObject:waitUntilDone:

7. 淺複製和深複製的區別?segmentfault

答:淺層複製:只複製指向對象的指針,而不復制引用對象自己。
深層複製:複製引用對象自己。

8. PerformSelecter後端

當調用 NSObject 的 performSelecter:afterDelay:後,實際上其內部會建立一個 Timer 並添加到當前線程的 RunLoop 中。因此若是當前線程沒有 RunLoop,則這個方法會失效。
當調用 performSelector:onThread:時,實際上其會建立一個 Timer 加到對應的線程去,一樣的,若是對應線程沒有 RunLoop 該方法也會失效。

9. 優化你是從哪幾方面着手?api

1、首頁啓動速度
啓動過程當中作的事情越少越好(儘量將多個接口合併)
不在UI線程上做耗時的操做(數據的處理在子線程進行,處理完通知主線程刷新節目)
在合適的時機開始後臺任務(例如在用戶指引節目就能夠開始準備加載的數據)
2、頁面瀏覽速度
json的處理(iOS 自帶的NSJSONSerialization,Jsonkit,SBJson)
數據的分頁(後端數據多的話,就要分頁返回,例如網易新聞,或者 微博記錄)
數據壓縮(大數據也能夠壓縮返回,減小流量,加快反應速度)
內容緩存(例如網易新聞的最新新聞列表都是要緩存到本地,從本地加載,能夠緩存到內存,或者數據庫,根據狀況而定)
延時加載tab(好比app有5個tab,能夠先加載第一個要顯示的tab,其餘的在顯示時候加載,按需加載)
算法的優化(核心算法的優化,例若有些app 有個 聯繫人姓名用漢語拼音的首字母排序)
3、操做流暢度優化
Tableview 優化(tableview cell的加載優化)
ViewController加載優化(不一樣view之間的跳轉,能夠提早準備好數據)
4、數據庫的優化
數據庫設計上面的重構
查詢語句的優化
分庫分表(數據太多的時候,能夠分不一樣的表或者庫)
5、服務器端和客戶端的交互優化
客戶端儘可能減小請求
服務端儘可能作多的邏輯處理
服務器端和客戶端採起推拉結合的方式(能夠利用一些同步機制)
通訊協議的優化(減小報文的大小)
電量使用優化(儘可能不要使用後臺運行)
6、非技術性能優化
產品設計的邏輯性(產品的設計必定要符合邏輯,或者邏輯儘可能簡單,不然會讓程序員抓狂,有時候用了好大力氣,才能夠完成一個小小的邏輯設計問題)
界面交互的規範(每一個模塊的界面的交互儘可能統一,符合操做習慣)
代碼規範(這個能夠隱形帶來app 性能的提升,好比 用if else 仍是switch ,或者是用!仍是 ==)
code review(堅持code Review 持續重構代碼。減小代碼的邏輯複雜度)

10. 什麼狀況使用 weak 關鍵字,相比 assign 有什麼不一樣?

1.在ARC中,在有可能出現循環引用的時候,每每要經過讓其中一端使用 weak 來解決,好比: delegate 代理屬性。
2.自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,如自定義 IBOutlet 控件屬性通常也使用 weak;固然,也可使用strong。
IBOutlet連出來的視圖屬性爲何能夠被設置成weak?
答:由於父控件的subViews數組已經對它有一個強引用。
不一樣點
assign 能夠用非 OC 對象,而 weak 必須用於 OC 對象。
weak 代表該屬性定義了一種「非擁有關係」。在屬性所指的對象銷燬時,屬性值會自動清空(nil)

11. 用@property聲明的 NSString / NSArray / NSDictionary 常用 copy 關鍵字,爲何?若是改用strong關鍵字,可能形成什麼問題?

答:用 @property 聲明 NSString、NSArray、NSDictionary 常用 copy 關鍵字,是由於他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操做(就是把可變的賦值給不可變的),爲確保對象中的字符串值不會無心間變更,應該在設置新屬性值時拷貝一份。
1.由於父類指針能夠指向子類對象,使用 copy 的目的是爲了讓本對象的屬性不受外界影響,使用 copy 不管給我傳入是一個可變對象仍是不可對象,我自己持有的就是一個不可變的副本。
2.若是咱們使用是 strong ,那麼這個屬性就有可能指向一個可變對象,若是這個可變對象在外部被修改了,那麼會影響該屬性。
總結:使用copy的目的是,防止把可變類型的對象賦值給不可變類型的對象時,可變類型對象的值發送變化會無心間篡改不可變類型對象原來的值。

12. runtime如何實現weak變量的自動置nil?

runtime對註冊的類,會進行佈局,會將 weak 對象放入一個 hash 表中。用 weak 指向的對象內存地址做爲 key,當此對象的引用計數爲0的時候會調用對象的 dealloc 方法,假設 weak 指向的對象內存地址是a,那麼就會以a爲key,在這個 weak hash表中搜索,找到全部以a爲key的 weak 對象,從而設置爲nil

13. runloop是什麼/runloop的概念?

runloop是線程相關的基礎框架的一部分。一個runloop就是一個事件處理的循環,用來不停的調度工做以及處理輸入事件。其實內部就是do-while循環,這個循環內部不斷地處理各類任務(好比Source,Timer,Observer)。使用runloop的目的是讓你的線程在有工做的時候忙於工做,而沒工做的時候處於休眠狀態。

14. UITableViewCell上有個UILabel,顯示NSTimer實現的秒錶時間,手指滾動cell過程當中,label是否刷新,爲何?

這是否刷新取決於timer加入到Run Loop中的Mode是什麼。Mode主要是用來指定事件在運行循環中的優先級的,分爲

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認,空閒狀態
  • UITrackingRunLoopMode:ScrollView滑動時會切換到該Mode
  • UIInitializationRunLoopMode:run loop啓動時,會切換到該mode
  • NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合
    蘋果公開提供的Mode有兩個
  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
  • NSRunLoopCommonModes(kCFRunLoopCommonModes)
    在編程中:若是咱們把一個NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運行循環中的時候, ScrollView滾動過程當中會由於mode的切換,而致使NSTimer將再也不被調度。當咱們滾動的時候,也但願不調度,那就應該使用默認模式。可是,若是但願在滾動時,定時器也要回調,那就應該使用common mode。

15. NStimer準嗎?談談你的見解?若是不許該怎樣實現一個精確的NSTimer?

不許;不許的緣由以下
一、NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main負責全部主線程事件,例如UI界面的操做,複雜的運算,這樣在同一個runloop中timer就會產生阻塞。
二、模式的改變。主線程的 RunLoop 裏有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。
當你建立一個 Timer 並加到 DefaultMode 時,Timer 會獲得重複回調,但此時滑動一個ScrollView時,RunLoop 會將 mode 切換爲 TrackingRunLoopMode,這時 Timer 就不會被回調,而且也不會影響到滑動操做。因此就會影響到NSTimer不許的狀況。
PS:DefaultMode 是 App 平時所處的狀態,rackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態。
方法:
一、在主線程中進行NSTimer操做,可是將NSTimer實例加到main runloop的特定mode(模式)中。避免被複雜運算操做或者UI界面刷新所幹擾self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
二、在子線程中進行NSTimer的操做,再在主線程中修改UI界面顯示操做結果
-(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 有哪些優點?

GCD是基於c的底層api,NSOperation屬於object-c類。ios 首先引入的是NSOperation,IOS4以後引入了GCD和NSOperationQueue而且其內部是用gcd實現的。
相對於GCD:
一、NSOperation擁有更多的函數可用,具體查看api。
二、在NSOperationQueue中,能夠創建各個NSOperation之間的依賴關係。
三、有kvo能夠監測operation是否正在執行(isExecuted)、是否結束(isFinished),是否取消(isCanceld)。
四、NSOperationQueue能夠方便的管理併發、NSOperation之間的優先級。
GCD主要與block結合使用。代碼簡潔高效。
GCD也能夠實現複雜的多線程應用,主要是創建個個線程時間的依賴關係這類的狀況,可是須要本身實現相比NSOperation要複雜。
具體使用哪一個,依需求而定。 從我的使用的感受來看,比較合適的用法是:除了依賴關係儘可能使用GCD,由於蘋果專門爲GCD作了性能上面的優化。

17. 如何訪問並修改一個類的私有屬性?

有兩種方法能夠訪問私有屬性,一種是經過KVC獲取,一種是經過runtime訪問並修改私有屬性。

18. 如何捕獲異常?

1\. 在app啓動時(didFinishLaunchingWithOptions),添加一個異常捕獲的監聽
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
2\. 實現捕獲異常日誌並保存到本地的方法
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];
    //平常日誌保存(能夠將此功能單獨提煉到一個方法中)
    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) {
        //此處可擴展
        NSString *logPath = [logDir stringByAppendingString:@"/crashLog.txt"];
        [excpCnt writeToFile:logPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
    }
}

19. Object-c的類能夠多重繼承麼?能夠實現多個接口麼?Category是什麼?重寫一個類的方式用繼承好仍是分類好?爲何?

答:Object-c的類不能夠多重繼承;能夠實現多個接口,經過實現多個接口能夠完成C++的多重繼承;Category是類別,通常狀況用分類好,用Category去重寫類的方法,僅對本Category有效,不會影響到其餘類與原有類的關係。

20. Category(分類),Extension(擴展)和繼承的區別

答:1.分類
category原則上只能在現有類基礎上添加新的方法(能添加屬性的緣由只是經過runtime解決無setter/getter的問題而已),類別中的方法沒被實現編譯器是不會有任何警告的,這是由於類別是在運行時添加到類中的
2.擴展
iOS中的extension就是匿名的分類,只有頭文件沒有實現文件。類擴展不只能夠增長方法,還能夠增長實例變量(或者屬性),只是該實例變量默認是@private類型的(使用範圍只能在自身類,而不是子類或其餘地方),類擴展中聲明的方法沒被實現,編譯器會報警,這是由於類擴展是在編譯階段被添加到類中的
3.繼承
在iOS中繼承是單繼承,既只能有一個父類。在繼承中,子類可使用父類的方法和變量,當子類想對本類或者父類的變量進行初始化,那麼須要重寫init()方法 。父類也能夠訪問子類的方法和成員變量

21. 簡述內存分區狀況

1).代碼區:存放函數二進制代碼
2).數據區:系統運行時申請內存並初始化,系統退出時由系統釋放。存放全局變量、靜態變量、常量
3).堆區:經過malloc等函數或new等操做符動態申請獲得,需程序員手動申請和釋放
4).棧區:函數模塊內申請,函數結束時由系統自動釋放。存放局部變量、函數參數

22. 直接調用_objc_msgForward函數將會發生什麼?

_objc_msgForward是 IMP 類型,用於消息轉發的:當向一個對象發送一條消息,但它並無實現的時候,_objc_msgForward會嘗試作消息轉發。
直接調用_objc_msgForward是很是危險的事,若是用很差會直接致使程序Crash,可是若是用得好,能作不少很是酷的事。
一旦調用_objc_msgForward,將跳過查找 IMP 的過程,直接觸發「消息轉發」,若是調用了_objc_msgForward,即便這個對象確實已經實現了這個方法,你也會告訴objc_msgSend:「我沒有在這個對象裏找到這個方法的實現」

23. 對於Run Loop的理解

  • RunLoop,是多線程的法寶,即一個線程一次只能執行一個任務,執行完任務後就會退出線程。主線程執行完即時任務時會繼續等待接收事件而不退出。非主線程一般來講就是爲了執行某一任務的,執行完畢就須要歸還資源,所以默認是不運行RunLoop的;
  • 每個線程都有其對應的RunLoop,只是默認只有主線程的RunLoop是啓動的,其它子線程的RunLoop默認是不啓動的,若要啓動則須要手動啓動;
  • 在一個單獨的線程中,若是須要在處理完某個任務後不退出,繼續等待接收事件,則須要啓用RunLoop;
  • NSRunLoop提供了一個添加NSTimer的方法,能夠指定Mode,若是要讓任何狀況下都回調,則須要設置Mode爲Common模式;
  • 實質上,對於子線程的runloop默認是不存在的,由於蘋果採用了懶加載的方式。若是咱們沒有手動調用[NSRunLoop currentRunLoop]的話,就不會去查詢是否存在當前線程的RunLoop,也就不會去加載,更不會建立。

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

1.每個類對象中都一個對象方法列表(對象方法緩存)
2.類方法列表是存放在類對象中isa指針指向的元類對象中(類方法緩存)
3.方法列表中每一個方法結構體中記錄着方法的名稱,方法實現,以及參數類型,其實selector本質就是方法名稱,經過這個方法名稱就能夠在方法列表中找到對應的方法實現.
4.當咱們發送一個消息給一個NSObject對象時,這條消息會在對象的類對象方法列表裏查找
5.當咱們發送一個消息給一個類時,這條消息會在類的Meta Class對象的方法列表裏查找

25. runtime 中,SEL 和 IMP 的區別

方法名 SEL – 表示該方法的名稱;
IMP – 指向該方法的具體實現的函數指針,說白了IMP就是實現方法。

26.block底層實現

block本質是指向一個結構體的一個指針
運行時機制 比較高級的特性 純C語言
平時寫的OC代碼 轉換成C語言運行時的代碼
指令:clang -rewrite-objc main.m(能夠打印驗證)
默認狀況下,任何block都是在棧裏面的,隨時可能被回收
只要對其作一次copy操做 block的內存就會放在堆裏面 不會釋放
只有copy才能產生一個新的內存地址 全部地址會發生改變

27. TCP協議三次握手

TCP協議採用了三次握手策略。用TCP協議把數據包送出去後,TCP不會對傳送後的狀況置之不理,它必定會向對方確認是否成功送達。握手過程當中使用了TCP的標誌——SYN(synchronize)和ACK(acknowledgement)。發送端首先發送一個帶SYN標誌的數據包給對方。接收端收到後,回傳一個帶有SYN/ACK標誌的數據包以示傳達確認信息。最後,發送端再回傳一個帶ACK標誌的數據包,表明「握手」結束。
TCP協議三次握手示意圖

28. @property 的本質是什麼?

@property = ivar + getter + setter;
「屬性」 (property)有兩大概念:ivar(實例變量)、getter+setter(存取方法)

29. KVC的底層實現?

當一個對象調用setValue方法時,方法內部會作如下操做:
1). 檢查是否存在相應的key的set方法,若是存在,就調用set方法。
2). 若是set方法不存在,就會查找與key相同名稱而且帶下劃線的成員變量,若是有,則直接給成員變量屬性賦值。
3). 若是沒有找到_key,就會查找相同名稱的屬性key,若是有就直接賦值。
4). 若是尚未找到,則調用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
這些方法的默認實現都是拋出異常,咱們能夠根據須要重寫它們。

30. ViewController生命週期

按照執行順序排列:
1). initWithCoder:經過nib文件初始化時觸發。
2). awakeFromNib:nib文件被加載的時候,會發生一個awakeFromNib的消息到nib文件中的每一個對象。
3). loadView:開始加載視圖控制器自帶的view。
4). viewDidLoad:視圖控制器的view被加載完成。
5). viewWillAppear:視圖控制器的view將要顯示在window上。
6). updateViewConstraints:視圖控制器的view開始更新AutoLayout約束。
7). viewWillLayoutSubviews:視圖控制器的view將要更新內容視圖的位置。
8). viewDidLayoutSubviews:視圖控制器的view已經更新視圖的位置。
9). viewDidAppear:視圖控制器的view已經展現到window上。
10). viewWillDisappear:視圖控制器的view將要從window上消失。
11). viewDidDisappear:視圖控制器的view已經從window上消失。

31. 如何用GCD同步若干個異步調用?(如根據若干個url異步加載多張圖片,而後在都下載完成後合成一張整圖)

// 使用Dispatch Group追加block到Global Group Queue,這些block若是所有執行完畢,就會執行Main Dispatch Queue中的結束處理的block。
// 建立隊列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局併發隊列
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 */ }); 
// 當併發隊列組中的任務執行完畢後纔會執行這裏的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合併圖片
});

32. dispatch_barrier_async(柵欄函數)的做用是什麼?

函數定義:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
做用:
    1.在它前面的任務執行結束後它才執行,它後面的任務要等它執行完成後纔會開始執行。
    2.避免數據競爭

// 1.建立併發隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向隊列中添加任務
dispatch_async(queue, ^{  // 1.2是並行的
    NSLog(@"任務1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任務2, %@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"任務 barrier, %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{   // 這兩個是同時執行的
    NSLog(@"任務3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任務4, %@",[NSThread currentThread]);
});

// 輸出結果: 任務1 任務2 ——》 任務 barrier ——》任務3 任務4 
// 其中的任務1與任務2,任務3與任務4 因爲是並行處理前後順序不定。

做者:72行代碼 連接:iOS 面試題彙總

相關文章
相關標籤/搜索