iOS 面試題

設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式,并簡要敘述?

設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型的事情。

  • MVC模式:Model View Control,把模型 視圖 控制器 層進(jìn)行解耦合編寫。
  • MVVM模式:Model View ViewModel 把模型 視圖 業(yè)務(wù)邏輯 層進(jìn)行解耦和編寫。
  • 單例模式:通過static關(guān)鍵詞,聲明全局變量。在整個(gè)進(jìn)程運(yùn)行期間只會(huì)被賦值一次。
  • 觀察者模式:KVO是典型的通知模式,觀察某個(gè)屬性的狀態(tài),狀態(tài)發(fā)生變化時(shí)通知觀察者。
  • 委托模式:代理+協(xié)議的組合。實(shí)現(xiàn)1對1的反向傳值操作。
  • 工廠模式:通過一個(gè)類方法,批量的根據(jù)已有模板生產(chǎn)對象。

MVC 和 MVVM 的區(qū)別

  • MVVM是對胖模型進(jìn)行的拆分,其本質(zhì)是給控制器減負(fù),將一些弱業(yè)務(wù)邏輯放到VM中去處理。
  • MVC是一切設(shè)計(jì)的基礎(chǔ),所有新的設(shè)計(jì)模式都是基于MVC進(jìn)行的改進(jìn)。

frame 和 bounds 有什么不同?

frame指的是:該view在父view坐標(biāo)系統(tǒng)中的位置和大小。(參照點(diǎn)是父view的坐標(biāo)系統(tǒng))
bounds指的是:該view在本身坐標(biāo)系統(tǒng)中的位置和大小。(參照點(diǎn)是本身坐標(biāo)系統(tǒng))

Objective-C 中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么?

線程創(chuàng)建有三種方法:

  • 使用NSThread創(chuàng)建
  • 使用GCD的dispatch
  • 使用子類化的NSOperation,然后將其加入NSOperationQueue;在主線程執(zhí)行代碼,方法是performSelectorOnMainThread,如果想延時(shí)執(zhí)行代碼可以performSelector:onThread:withObject:waitUntilDone:

Category(類別)、 Extension(擴(kuò)展)和繼承的區(qū)別

區(qū)別:

  • 分類有名字,類擴(kuò)展沒有分類名字,是一種特殊的分類。
  • 分類只能擴(kuò)展方法(屬性僅僅是聲明,并沒真正實(shí)現(xiàn)),類擴(kuò)展可以擴(kuò)展屬性、成員變量和方法。
  • 繼承可以增加,修改或者刪除方法,并且可以增加屬性。

如何對iOS設(shè)備進(jìn)行性能測試?

  • Profile-> Instruments ->Time Profiler

開發(fā)項(xiàng)目時(shí)你是怎么檢查內(nèi)存泄露?

  • 靜態(tài)分析 analyze。
  • instruments工具里面有個(gè)leak可以動(dòng)態(tài)分析。

什么是block?

閉包(block):閉包就是獲取其它函數(shù)局部變量的匿名函數(shù)。

  • block反向傳值
  • 在控制器間傳值可以使用代理或者block,使用block相對來說簡潔。

block的注意點(diǎn)

  • 在block內(nèi)部使用外部指針且會(huì)造成循環(huán)引用情況下,需要用__week修飾外部指針: __weak typeof(self) weakSelf = self;
  • 在block內(nèi)部如果調(diào)用了延時(shí)函數(shù)還使用弱指針會(huì)取不到該指針,因?yàn)橐呀?jīng)被銷毀了,需要在block內(nèi)部再將弱指針重新強(qiáng)引用一下。 __strong typeof(self) strongSelf = weakSelf;
  • 如果需要在block內(nèi)部改變外部棧區(qū)變量的話,需要在用__block修飾外部變量。

你一般是怎么用Instruments的?

  • Time Profiler: 性能分析
  • Zombies:檢查是否訪問了僵尸對象,但是這個(gè)工具只能從上往下檢查,不智能。
  • Allocations:用來檢查內(nèi)存,寫算法的那批人也用這個(gè)來檢查。
  • Leaks:檢查內(nèi)存,看是否有內(nèi)存泄露。

iOS中常用的數(shù)據(jù)存儲(chǔ)方式有哪些?

數(shù)據(jù)存儲(chǔ)有四種方案:NSUserDefault、KeyChain、file、DB。
其中File有三種方式:plist、Archive(歸檔)
DB包括:SQLite、FMDB、CoreData

GCD 與 NSOperation 的區(qū)別:

GCD 和 NSOperation 都是用于實(shí)現(xiàn)多線程
GCD 基于C語言的底層API,GCD主要與block結(jié)合使用,代碼簡潔高效。
NSOperation 屬于Objective-C類,是基于GCD更高一層的封裝。復(fù)雜任務(wù)一般用NSOperation實(shí)現(xiàn)。

寫出使用GCD方式從子線程回到主線程的方法代碼

dispatch_sync(dispatch_get_main_queue(), ^{ });

如何用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(), ^{         // 合并圖片 });

什么是 RunLoop

從字面上講就是運(yùn)行循環(huán),它內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)。
一個(gè)線程對應(yīng)一個(gè)RunLoop,基本作用就是保持程序的持續(xù)運(yùn)行,處理app中的各種事件。通過runloop,有事運(yùn)行,沒事就休息,可以節(jié)省cpu資源,提高程序性能。

什么是 Runtime

Runtime又叫運(yùn)行時(shí),是一套底層的C語言API,其為iOS內(nèi)部的核心之一,我們平時(shí)編寫的OC代碼,底層都是基于它來實(shí)現(xiàn)的。

Runtime實(shí)現(xiàn)的機(jī)制是什么,怎么用,一般用于干嘛?

  • 使用時(shí)需要導(dǎo)入的頭文件 <objc/message.h> <objc/runtime.h>
  • Runtime 運(yùn)行時(shí)機(jī)制,它是一套C語言庫。
  • 實(shí)際上我們編寫的所有OC代碼,最終都是轉(zhuǎn)成了runtime庫的東西。
    比如:

類轉(zhuǎn)成了 Runtime 庫里面的結(jié)構(gòu)體等數(shù)據(jù)類型
方法轉(zhuǎn)成了 Runtime 庫里面的C語言函數(shù)
平時(shí)調(diào)方法都是轉(zhuǎn)成了 objc_msgSend 函數(shù)(所以說OC有個(gè)消息發(fā)送機(jī)制)
OC是動(dòng)態(tài)語言,每個(gè)方法在運(yùn)行時(shí)會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)。
[stu show]; 在objc動(dòng)態(tài)編譯時(shí),會(huì)被轉(zhuǎn)意為:objc_msgSend(stu, @selector(show));

  • 因此,可以說 Runtime 是OC的底層實(shí)現(xiàn),是OC的幕后執(zhí)行者。

有了Runtime庫,能做什么事情呢?

Runtime庫里面包含了跟類、成員變量、方法相關(guān)的API。
比如:

  • 獲取類里面的所有成員變量。
  • 為類動(dòng)態(tài)添加成員變量。
  • 動(dòng)態(tài)改變類的方法實(shí)現(xiàn)。
  • 為類動(dòng)態(tài)添加新的方法等。

因此,有了Runtime,想怎么改就怎么改。

什么是 TCP / UDP ?

  • TCP:傳輸控制協(xié)議。
    TCP 是面向連接的,建立連接需要經(jīng)歷三次握手,是可靠的傳輸層協(xié)議。
  • UDP:用戶數(shù)據(jù)協(xié)議。
    UDP 是面向無連接的,數(shù)據(jù)傳輸是不可靠的,它只管發(fā),不管收不收得到。 簡單的說,TCP注重?cái)?shù)據(jù)安全,而UDP數(shù)據(jù)傳輸快點(diǎn),但安全性一般。

tableView的重用機(jī)制?

UITableView 通過重用單元格來達(dá)到節(jié)省內(nèi)存的目的: 通過為每個(gè)單元格指定一個(gè)重用標(biāo)識(shí)符,即指定了單元格的種類,當(dāng)屏幕上的單元格滑出屏幕時(shí),系統(tǒng)會(huì)把這個(gè)單元格添加到重用隊(duì)列中,等待被重用,當(dāng)有新單元格從屏幕外滑入屏幕內(nèi)時(shí),從重用隊(duì)列中找看有沒有可以重用的單元格,如果有,就拿過來用,如果沒有就創(chuàng)建一個(gè)來使用。

你是怎么封裝一個(gè)view的

  • 可以通過純代碼或者xib的方式來封裝子控件
  • 建立一個(gè)跟view相關(guān)的模型,然后將模型數(shù)據(jù)傳給view,通過模型上的數(shù)據(jù)給view的子控件賦值
/**  *  純代碼初始化控件時(shí)一定會(huì)走這個(gè)方法  */ 
- (instancetype)initWithFrame:(CGRect)frame
 {    
   if(self = [super initWithFrame:frame]) 
    {         
      [self setupUI];    
    }    
     return self; 
}  
/**  *  通過xib初始化控件時(shí)一定會(huì)走這個(gè)方法  */ 
- (id)initWithCoder:(NSCoder *)aDecoder {   
  if(self = [super initWithCoder:aDecoder]) {
         [self setupUI];    
   }   
  return self; 
}  
- (void)setupUI { 
    // 初始化代碼 
}

第三方框架

AFNetworking 底層原理分析

AFNetworking主要是對NSURLSession和NSURLConnection(iOS9.0廢棄)的封裝,其中主要有以下類:

  • AFHTTPRequestOperationManager:內(nèi)部封裝的是 NSURLConnection, 負(fù)責(zé)發(fā)送網(wǎng)絡(luò)請求, 使用最多的一個(gè)類。(3.0廢棄)
  • AFHTTPSessionManager:內(nèi)部封裝是 NSURLSession, 負(fù)責(zé)發(fā)送網(wǎng)絡(luò)請求,使用最多的一個(gè)類。
  • AFNetworkReachabilityManager:實(shí)時(shí)監(jiān)測網(wǎng)絡(luò)狀態(tài)的工具類。當(dāng)前的網(wǎng)絡(luò)環(huán)境發(fā)生改變之后,這個(gè)工具類就可以檢測到
  • AFSecurityPolicy:網(wǎng)絡(luò)安全的工具類, 主要是針對 HTTPS 服務(wù)。
  • AFURLRequestSerialization:序列化工具類,基類。上傳的數(shù)據(jù)轉(zhuǎn)換成JSON格式 (AFJSONRequestSerializer).使用不多。
  • AFURLResponseSerialization:反序列化工具類;基類.使用比較多
  • AFJSONResponseSerializer; JSON解析器,默認(rèn)的解析器.
  • AFHTTPResponseSerializer; 萬能解析器; JSON和XML之外的數(shù)據(jù)類型,直接返回二進(jìn) 制數(shù)據(jù).對服務(wù)器返回的數(shù)據(jù)不做任何處理.
  • AFXMLParserResponseSerializer; XML解析器;

描述下SDWebImage里面給UIImageView加載圖片的邏輯

SDWebImage 中為 UIImageView 提供了UIImageView+WebCache.h,
這個(gè)分類中有一個(gè)最常用的接口
sd_setImageWithURL:placeholderImage:會(huì)在真實(shí)圖片出現(xiàn)前會(huì)先顯示占位圖片,當(dāng)真實(shí)圖片被加載出來后再替換占位圖片。
加載圖片的過程大致如下:

  • 首先會(huì)在 SDWebImageCache 中尋找圖片是否有對應(yīng)的緩存, 它會(huì)以url 作為數(shù)據(jù)的索引先在內(nèi)存中尋找是否有對應(yīng)的緩存
  • 如果緩存未找到就會(huì)利用通過MD5處理過的key來繼續(xù)在磁盤中查詢對應(yīng)的數(shù)據(jù), 如果找到了, 就會(huì)把磁盤中的數(shù)據(jù)加載到內(nèi)存中,并將圖片顯示出來
  • 如果在內(nèi)存和磁盤緩存中都沒有找到,就會(huì)向遠(yuǎn)程服務(wù)器發(fā)送請求,開始下載圖片
  • 下載后的圖片會(huì)加入緩存中,并寫入磁盤中
  • 整個(gè)獲取圖片的過程都是在子線程中執(zhí)行,獲取到圖片后回到主線程將圖片顯示出來

SDWebImage原理: 調(diào)用類別的方法: 1. 從內(nèi)存(字典)中找圖片(當(dāng)這個(gè)圖片在本次使用程序的過程中已經(jīng)被加載過),找到直接使用。 2. 從沙盒中找(當(dāng)這個(gè)圖片在之前使用程序的過程中被加載過),找到使用,緩存到內(nèi)存中。 3. 從網(wǎng)絡(luò)上獲取,使用,緩存到內(nèi)存,緩存到沙盒。

編碼格式(優(yōu)化細(xì)節(jié))

在 Objective-C 中,enum 建議使用 NS_ENUM 和 NS_OPTIONS 宏來定義枚舉類型。

//定義一個(gè)枚舉(比較嚴(yán)密)
typedef NS_ENUM(NSInteger, BRUserGender) { 
    BRUserGenderUnknown,    // 未知     
    BRUserGenderMale,       // 男性 
    BRUserGenderFemale,      // 女性    
    BRUserGenderNeuter      // 無性 
};
@interface BRUser : NSObject<NSCopying> 
@property (nonatomic, readonly, copy) NSString *name; 
@property (nonatomic, readonly, assign) NSUInteger age; 
@property (nonatomic, readonly, assign) BRUserGender gender; 
 - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender;  
@end  

避免使用C語言中的基本數(shù)據(jù)類型,建議使用 Foundation 數(shù)據(jù)類型

對應(yīng)關(guān)系如下:
int -> NSInteger unsigned -> NSUInteger float -> CGFloat 動(dòng)畫時(shí)間 -> NSTimeInterval

如何實(shí)行cell的動(dòng)態(tài)的行高

如果希望每條數(shù)據(jù)顯示自身的行高,必須設(shè)置兩個(gè)屬性,

  • 預(yù)估行高
    設(shè)置預(yù)估行高 tableView.estimatedRowHeight = 200。
  • 自定義行高
    設(shè)置定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。

如果要讓自定義行高有效,必須讓容器視圖有一個(gè)自下而上的約束。

用StoryBoard開發(fā)界面有什么弊端?如何避免?

使用簡單邏輯頁面的跳轉(zhuǎn)是可以使用sb的,開發(fā)比較塊。但是SB對于邏輯項(xiàng)目比較復(fù)雜的時(shí)候,開發(fā)起來比較慢。
不適合多人合作開發(fā);也不利于版本的梗系和后期的維護(hù)。
使用sb在項(xiàng)目變異編譯的時(shí)候,也都會(huì)直接加載到內(nèi)存中,造成內(nèi)存的浪費(fèi)。 可以使用xib來代替,編輯復(fù)雜邏輯界面時(shí)候可以使用純碼編寫。

Swift

類(class)和結(jié)構(gòu)體(struct)有什么區(qū)別?

Swift 中,類是引用類型,結(jié)構(gòu)體是值類型。值類型在傳遞和賦值時(shí)將進(jìn)行復(fù)制,而引用類型則只會(huì)使用引用對象的一個(gè)"指向"。所以他們兩者之間的區(qū)別就是兩個(gè)類型的區(qū)別。

請說明并比較以下關(guān)鍵詞:Open, Public, Internal, File-private, Private

Swift 有五個(gè)級別的訪問控制權(quán)限,從高到底依次為比如 Open, Public, Internal, File-private, Private。
基本原則:高級別的變量不允許被定義為低級別變量的成員變量。
比如一個(gè) private 的 class 中不能含有 public 的 String。反之,低級別的變量卻可以定義在高級別的變量中。比如 public 的 class 中可以含有 private 的 Int。

  • Open 具備最高的訪問權(quán)限。其修飾的類和方法可以在任意 Module 中被訪問和重寫;它是 Swift 3 中新添加的訪問權(quán)限。
  • Public 的權(quán)限僅次于 Open。與 Open 唯一的區(qū)別在于它修飾的對象可以在任意 Module 中被訪問,但不能重寫。
  • Internal 是默認(rèn)的權(quán)限。它表示只能在當(dāng)前定義的 Module 中訪問和重寫,它可以被一個(gè) Module 中的多個(gè)文件訪問,但不可以被其他的 Module 中被訪問。
  • File-private 也是 Swift 3 新添加的權(quán)限。其被修飾的對象只能在當(dāng)前文件中被使用。例如它可以被一個(gè)文件中的 class,extension,struct 共同使用。
  • Private 是最低的訪問權(quán)限。它的對象只能在定義的作用域內(nèi)使用。離開了這個(gè)作用域,即使是同一個(gè)文件中的其他作用域,也無法訪問。

請說明并比較以下關(guān)鍵詞:strong, weak, unowned

Swift 的內(nèi)存管理機(jī)制與 Objective-C一樣為 ARC(Automatic Reference Counting)。
它的基本原理是,一個(gè)對象在沒有任何強(qiáng)引用指向它時(shí),其占用的內(nèi)存會(huì)被回收。反之,只要有任何一個(gè)強(qiáng)引用指向該對象,它就會(huì)一直存在于內(nèi)存中。

  • strong 代表著強(qiáng)引用,是默認(rèn)屬性。
    當(dāng)一個(gè)對象被聲明為 strong 時(shí),就表示父層級對該對象有一個(gè)強(qiáng)引用的指向。此時(shí)該對象的引用計(jì)數(shù)會(huì)增加1。
  • weak 代表著弱引用。
    當(dāng)對象被聲明為 weak 時(shí),父層級對此對象沒有指向,該對象的引用計(jì)數(shù)不會(huì)增加1。它在對象釋放后弱引用也隨即消失。繼續(xù)訪問該對象,程序會(huì)得到 nil,不虧崩潰
  • unowned 與弱引用本質(zhì)上一樣。
    唯一不同的是,對象在釋放后,依然有一個(gè)無效的引用指向?qū)ο?,它不?Optional 也不指向 nil。如果繼續(xù)訪問該對象,程序就會(huì)崩潰。
    加分回答
    weak 和 unowned 的引入是為了解決由 strong 帶來的循環(huán)引用問題。
    簡單來說,就是當(dāng)兩個(gè)對象互相有一個(gè)強(qiáng)指向去指向?qū)Ψ?,這樣導(dǎo)致兩個(gè)對象在內(nèi)存中無法釋放。

weak 和 unowned 的使用場景有如下差別:

  • 當(dāng)訪問對象時(shí)該對象可能已經(jīng)被釋放了,則用 weak。比如 delegate 的修飾。
  • 當(dāng)訪問對象確定不可能被釋放,則用 unowned。比如 self 的引用。
  • 實(shí)際上為了安全起見,很多公司規(guī)定任何時(shí)候都使用 weak 去修飾。

在Swift和Objective-C的混編項(xiàng)目中,如何在Swift文件中調(diào)用Objective-C文件中已經(jīng)定義的方法?如何在Objective-C文件中調(diào)用Swift文件中定義的方法?

  • Swift中若要使用Objective-C代碼,可以在ProjectName-Bridging-Header.h里添加Objective-C的頭文件名稱,Swift文件中即可調(diào)用相應(yīng)的Objective-C代碼。
    一般情況Xcode會(huì)在Swift項(xiàng)目中第一次創(chuàng)建Objective-C文件時(shí)自動(dòng)創(chuàng)建ProjectName-Bridging-Header.h文件。
  • Objective-C中若要調(diào)用Swift代碼,可以導(dǎo)入Swift生成的頭函數(shù)ProjectName-Swift.h來實(shí)現(xiàn)。
  • Swift文件中若要規(guī)定固定的方法或?qū)傩员┞督oObjective-C使用,可以在方法或?qū)傩郧凹由螥objc來聲明。如果該類是NSObject子類,那么Swift會(huì)在非private的方法或?qū)傩郧白詣?dòng)加上@objc。

實(shí)現(xiàn)一個(gè)函數(shù)。求一個(gè)整型二維數(shù)組中所有元素之和

func sumPairs(_ nums: [[Int]]) -> Int {
  return nums.flatMap { $0 }.reduce(0) { $0 + $1 }
}

Swift 有函數(shù)式編程的思想。其中 flatMap, map, reduce, filter 是其代表的方法。
本題中考察了 flatMap 的降維思路,以及 reduce 的基本使用。相比于一般的 for 循環(huán),這樣的寫法要更加得簡潔漂亮。

項(xiàng)目

  • 有已經(jīng)上線的項(xiàng)目么?
  • 項(xiàng)目里哪個(gè)部分是你完成的?(找一個(gè)亮點(diǎn)問一下如何實(shí)現(xiàn)的)
  • 開發(fā)過程中遇到過什么困難,是如何解決的?

學(xué)習(xí)

  • 遇到一個(gè)問題完全不能理解的時(shí)候,是如何幫助自己理解的?舉個(gè)例子?
  • 有看書的習(xí)慣么?最近看的一本是什么書?有什么心得?
  • 有沒有使用一些筆記軟件?會(huì)在多平臺(tái)同步以及多渠道采集么?(如果沒有,問一下是如何復(fù)習(xí)知識(shí)的)
  • 有沒有使用清單類,日歷類的軟件?(如果沒有,問一下是如何安排,計(jì)劃任務(wù)的)
  • 平??床┛兔??有沒有自己寫過?(如果寫,有哪些收獲?如果沒有寫,問一下不寫的原因)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容