iOS面試珠璣

Fabric發表於2019-03-03

iOS基礎類

這裡包含了去哪兒,滴滴,螞蟻金服,美團,今日頭條,快手以及其它公司的一些面試題,大部分面試官問的重複的問題很多,總體來說就是以下的面試題。

  1. 簡述iOS中的記憶體管理方式
  • iOS的記憶體管理用的是引用計數的方法,分為MRC(手動引用計數)和ARC(自動引用計數)。

  • MRC:開發者手動地進行retain和release操作,對每個物件的retainCount進行+1,-1操作,當retainCount為0時,系統會自動釋放物件記憶體。

  • ARC:開發者通過宣告物件的屬性為strong,weak,retain,assign來管理物件的引用計數,被strong和retain修飾的屬性變數系統會自動對所修飾變數的引用計數進行自增自減操作,同樣地,retainCount為0時,系統會釋放物件記憶體。


  1. block的分類,__block的作用,block迴圈引用產生的原因及解決辦法
  • blcok分為全域性blcok,堆block,棧block。
  • 在 MRC下:只要沒有訪問外部變數,就是全域性block。訪問了外部變數,就是棧block。顯示地呼叫[block copy]就是堆block。
  • 在 ARC下:只要沒有訪問外部變數,就是全域性block。如果訪問了外部變數,那麼在訪問外部變數之前儲存在棧區,訪問外部變數之後儲存在堆區。
  • __block的作用:將外部變數的傳遞形式由值傳遞變為指標傳遞,從而可以獲取並且修改外部變數的值。同樣,外部變數的修改,也會影響block函式的輸出。
  • block迴圈引用問題:當一個類的物件持有block,block裡面又引用了這個物件,那麼就是一個迴圈引用的關係。可以用strong-weak-dance的方法解除迴圈引用。

3.深拷貝與淺拷貝

  • 深拷貝就是開闢一塊新的記憶體空間來儲存原來記憶體空間的內容,物件指標指向新的記憶體空間。淺拷貝只是重新生成一個指標,指向的還是原來的記憶體空間。

  • copy方法:如果是非可擴充套件類物件,則是淺拷貝。如果是可擴充套件類物件,則是深拷貝。

  • mutableCopy方法:無論是可擴充套件類物件還是不可擴充套件類物件,都是深拷貝。

  • 注意:深拷貝和深複製不同,深拷貝的記憶體空間的元素還是指向原地址,但是深複製會開闢新的記憶體空間重新複製子元素。


4.iOS中常見的屬性和預設的物件屬性

  • 常見屬性: atomic, nonatomic, assign, retain, strong, weak, copy, readonly, readwrite, unsafe_unretained, getter=, setter= 等。

  • 預設屬性: 繼承於NSObject類的物件:(atomic, strong), 非繼承於NSObject類的物件:(atomic, assign)

  • 屬性意義:

    atomic:原子性的,在執行setter和getter方法時可以保證訪問變數的執行緒安全。
    nonatomic:非原子性的,無法保證訪問變數的執行緒安全性,但是變數訪問效率會提高。
    assign:主要用於修飾非繼承於NSObject型別的物件,例如int, double, NSInteger等,當該物件被其他物件引用時,該物件的引用計數不會自加1。
    retain:一般情況下等同於ARC環境下的strong修飾符,但是在修飾block物件時,retain相當於assign,而strong相當於copy。
    strong:主要用於繼承於NSObject類的物件,當strong修飾的物件被其他物件引用時,引用計數會自加1。
    weak:主要用於繼承於NSObject類的物件,當weak修飾的物件被其他物件引用時,引用計數不會自加1,且retainCount為0時,指向該物件的指標將會置nil,指向堆疊的底部0x00000000,防止野指標的出現。
    unsafe_unretained:主要用於繼承於NSObject型別的物件,當retainCount為0時,指向該物件的指標不會置nil,因此可能會出現野指標,但是效率方面會比weak要高。
    copy:主要適用於NSArray, NSDictionary, NSString, Block等型別的物件,開闢一塊新的記憶體儲存原來記憶體中的元素,物件指標指向新記憶體的地址。
    readonly: 只讀屬性
    readwrite: 讀寫屬性
    getter = : 修改讀屬性名稱
    setter = : 修改寫屬性名稱

5.哪些屬性需要宣告成copy,為什麼?

  • NSDictionary, NSArray, NSString, Block需要宣告為copy屬性修飾,block用copy修飾是因為block只有拷貝到堆上才能保證呼叫block的時候,block沒有被系統提前釋放掉。NSDictionary, NSArray, NSString用copy修飾的原因直接上程式碼(以NSString舉例)
@interface ViewController ()
@property (nonatomic, copy)NSString *aStr;
@property (nonatomic, strong)NSString *bStr;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
//    [self buildUI];
    [self testCode];
}

- (void)testCode{
    
    NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"%@", @"abc"];
    self.aStr = mutableStr;
    self.bStr = mutableStr;
    [mutableStr appendString:@"123"];
    NSLog(@"copy修飾的字串:%@", self.aStr);
    NSLog(@"strong修飾的字串:%@", self.bStr);
}
複製程式碼

列印結果:

2018-05-22 21:13:58.344883 TTFeedBackDemo[1224:245923] copy修飾的字串:abc
2018-05-22 21:13:58.345004 TTFeedBackDemo[1224:245923] strong修飾的字串:abc123
複製程式碼

用了copy屬性修飾之後,可以防止這些型別的物件被引用並且改變內容。


  1. 通知,代理,block,KVO的使用場景分別是什麼,有什麼區別?
  • 通知: 適用於毫無關聯的頁面之間或者系統訊息的傳遞,屬於一對多的資訊傳遞關係。例如系統音量的改變,系統狀態的改變,應用模式的設定和改變,都比較適合用通知去傳遞資訊。

  • 代理: 一對一的資訊傳遞方式,適用於相互關聯的頁面之間的資訊傳遞,例如push和present出來的頁面和原頁面之間的資訊傳遞。

  • block: 一對一的資訊傳遞方式,效率會比代理要高(畢竟是直接取IMP指標的操作方式)。適用的場景和代理差不多,都是相互關聯頁面之間的頁面傳值。

  • KVO: 屬性監聽,監聽物件的某一屬性值的變化狀況,當需要監聽物件屬性改變的時候使用。例如在UIScrollView中,監聽contentOffset,既可以用KVO,也可以用代理。但是其他一些情況,比如說UIWebView的載入進度,AVPlayer的播放進度,就只能用KVO來監聽了,否則獲取不到對應的屬性值。


  1. 簡述對OC中的isa指標的認識
  • isa指標:首先,貼出NSObject.h檔案,大家巨集觀感受一下。

    iOS面試珠璣
    Ojective-C語言是基於C語言的封裝,它實現了將程式導向的語言向物件導向的語言的轉變。而OC中絕大部分類又是繼承於NSObject類的,所以研究清楚NSObject類的構成,對於理解OC語言很有幫助。

    NSObject類引入了兩個標頭檔案:#include <objc/objc.h>#include <objc/NSObjCRuntime.h>,第一個標頭檔案引入的是objc結構體的構成方式即isa指標,第二個標頭檔案引入的是Runtime訊息查詢機制。
    接下來又引入了三個類的宣告:@class NSString, NSMethodSignature, NSInvocation;
    NSMethodSignature和NSInvacation和OC的方法轉發機制有關,而NSString是與NSObject的+ (NSString *)description方法有關。

    isa指標: 程式碼如下

    @interface NSObject <NSObject> {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
    }
    複製程式碼

    NSObject物件其實包含了一個Class型別的isa指標。 這個和id型別是一樣的,程式碼如下:

    struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    複製程式碼

    即id型別其實是objc_object指標型別的別稱。objc_object結構體同樣也是包含了一個指向Class型別的指標。 繼續看一下,Class isa指標的內部結構,如下

    struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;//指向元類的Class指標
    
    #if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;//指向父類的Class指標
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;//類名
    long version                                             OBJC2_UNAVAILABLE;//類的版本資訊,預設為0
    long info                                                OBJC2_UNAVAILABLE;//執行期使用的一些位標識
    long instance_size                                       OBJC2_UNAVAILABLE;//該類的例項變數大小
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;//屬性列表
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;//方法列表
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;//快取方法列表
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;//協議列表
    #endif
    
    } OBJC2_UNAVAILABLE;
    
    typedef struct objc_class *Class;
    複製程式碼

    Class其實就是一個objc_class型別的結構體指標。objc_class的結構體變數構成見上圖,下面是objc_class類的super_class指標和isa元類指標的具體指向關係,請大家分清物件,物件的類,元類,根元類這些概念。

    iOS面試珠璣


  1. 簡述OC中的訊息轉發機制
  • 當objc_msgSend方法呼叫找不到響應的函式名稱時就會進行訊息轉發,主要分為3步:

    1、動態方法解析

    呼叫方法+(BOOL)resolveInstanceMethod:(SEL)sel(例項方法動態解析)和+ (BOOL)resolveClassMethod:(SEL)sel(類方法動態解析)。

    2、備援接收者

    呼叫方法 - (id)forwardingTargetForSelector:(SEL)aSelector

    3、完全轉發

    呼叫方法- (void)forwardInvocation:(NSInvocation *)anInvocation- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

    具體的轉發流程見下圖:

iOS面試珠璣


  1. 響應鏈原理
  • 當使用者觸控螢幕時,觸碰螢幕產生事件UIEvent並存入UIApplication中的事件佇列中, 並且在整個檢視結構中自上而下的進行分發,如下圖所示:

    iOS面試珠璣

  • 這裡著重介紹兩個方法

//判斷當前點選事件是否存在最優響應者(First Responder)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
複製程式碼
//判斷當前點選是否在控制元件的Bounds之內
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
複製程式碼

第一個方法返回一個可以響應event觸控事件的UIView,第二個方法判斷觸控點位在不在可相應範圍之內的BOOL值。所以會衍生出一些問題,比如說“如何讓一個父檢視以外的子檢視響應點選事件”,“如何只讓一個UIView的圓形區域響應觸控事件”等等,在此由於篇幅限制,不再一一展開詳述。下面這幅圖簡述了系統查詢響應事件控制元件的流程。

iOS面試珠璣


  1. 手寫一個block結構體宣告
  • 這就是考察一名iOS開發人員的寫碼基本功了,大家加深印象即可: typedef void(^RBBlogDemoHandler)(void);

  1. RunLoop原理,RunLoop與執行緒的關係
  • RunLoop其實就是一個do-while迴圈,Runloop的存在保證了程式一直在前臺執行。RunLoop和執行緒是一一對應的關係,即開啟一個執行緒,就會建立一個RunLoop對執行緒進行處理和管理,直到Runloop中沒有需要處理的item,RunLoop才會進入休眠狀態,如果休眠一段時間沒有被喚醒的話,RunLoop將會被銷燬掉。RunLoop的執行邏輯見下圖:
    iOS面試珠璣

  1. GCD與NSOperation兩種管理多執行緒方式的異同點
  • GCD是用C語言實現的,而NSOperation是用OC實現的。
  • NSOperation可以設定最大執行緒併發數,可以設定執行緒依賴關係,可以設定執行緒的優先順序。
  • GCD方式管理多執行緒是一種對開發者非常友好的開發方式,開發者只需要關注同步非同步,序列併發這些執行緒關係就可以輕鬆地進行執行緒管理了。另外,執行緒中執行的任務是通過Block的形式呼叫的,所以執行效率也是非常高的。

  1. GCD的常用api
  • dispatch_sync:同步執行緒函式
  • dispatch_async:非同步執行緒函式
  • dispatch_group_async:群執行緒函式
  • dispatch_barrier_async:柵欄函式,阻塞所屬的佇列的執行緒
  • dispatch_barrier_sync:柵欄函式,阻塞當前執行緒
  • dispatch_apply:該函式按指定的次數將指定的Block追加到指定的Dispatch Queue中,並等待全部處理執行結束。

  1. GCD中的同步非同步,序列併發的概念,GCD常見的執行緒死鎖問題
  • 同步:針對於執行緒而言的概念,阻塞當前執行緒,不執行結束,當前執行緒就不會繼續往下執行。
  • 非同步:針對於執行緒而言的概念,不阻塞當前執行緒,當前執行緒會繼續往下執行。
  • 序列:針對於佇列而言,序列佇列遵守FIFO(first-in-first-out)原則,只能一個任務一個任務地順序執行。
  • 併發:針對於佇列而言,併發佇列可以在同一時間執行多個任務。
  • 類似於
dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"%@", @"123");
    });
複製程式碼

這樣的函式,就是典型的GCD死鎖函式。 因為dispatch_sync阻塞的當前的執行緒,而當前執行緒是main_queue,也就是說是一個序列執行緒,當前執行緒只有先執行NSLog(@"%@", @"123")才能繼續執行下去,但是當前執行緒又被阻塞掉了,無法向下繼續執行,所以這就是一個死鎖的GCD執行函式了。


  1. iOS中常用的執行緒鎖有哪些,分別具有哪些特點?
  • @synchronized 關鍵字加鎖 互斥鎖,效能較差不推薦使用
  • NSLock 互斥鎖 不能多次呼叫 lock方法,會造成死鎖
  • NSRecursiveLock遞迴鎖,NSRecursiveLock類定義的鎖可以在同一執行緒多次lock,而不會造成死鎖。遞迴鎖會跟蹤它被多少次lock。每次成功的lock都必須平衡呼叫unlock操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其他執行緒獲得。
  • NSConditionLock條件鎖,顧名思義,這個鎖物件有一個condition屬性,只有condition的值相同,才能獲取到該鎖,並且執行解鎖操作。
  • POSIX互斥鎖,POSIX是Unix/Linux平臺上提供的一套條件互斥鎖的API。用法和特點與NSLock類似。
  • dispatch_semaphore訊號量實現加鎖,當訊號量為0時,執行緒將會被卡死,通過訊號量的增減來達到控制執行緒個數的目的。
  • OSSpinLock自旋鎖,用法類似於NSLock,可以自動檢查執行緒鎖是否已經開啟,效率比較高,但是被證明不是執行緒安全的。
  • GCD執行緒阻斷dispatch_barrier_async/dispatch_barrier_syncdispatch_barrier_async/dispatch_barrier_sync在一定的基礎上也可以做執行緒同步,會線上程佇列中打斷其他執行緒執行當前任務。兩個的區別是dispatch_barrier_async阻塞的是當前佇列的執行緒,而dispatch_barrier_sync阻塞的是任務所插入佇列的執行緒。
  • 各種執行緒鎖的執行效率對比如下圖:
    iOS面試珠璣

  1. Father的子類Son,分別寫出NSStringFromClass([self class]),NSStringFromClass([super class]),NSStringFromClass(self.superClass)的列印值
  • [self class] : Son
  • [super class] : Son
  • self.superClass : Father
  • 第一個就不用解釋了,列印本類的類名。
  • [super class]找到NSObject中class方法以後,reciever不變。實際上是因為super只是一個“編譯器指示符”,它和self指向的是相同的receiver。這裡要深刻理解super所指代的上下文環境。
  • self.superClass是取到了本類的superClass指標,所以列印的是父類的類名。

  1. KVO的原理
  • 例如,A類的例項的name屬性被B類的例項監聽了。這時,OC的runtime機制生成了一個KVONotifying_A的類來替代原來的A類,重寫了+ (Class)class方法,返回[A Class],從而把自己偽裝成A類。重寫了A類屬性name的setter方法加入了NSObject的兩個方法:willChangeValueForKey:(值改變之前)和didChangevlueForKey:(值改變之後)。在一個被觀察屬性發生改變之前,willChangeValueForKey:一定會被呼叫,這就會記錄舊的值。而當改變發生後,didChangeValueForKey:會被呼叫,繼而observeValueForKey:ofObject:change:context:也會被呼叫。

  1. runtime的機制和應用
  • Objective-C是一門動態強型別語言,它會將一些工作放在程式碼執行時才處理而並非編譯時。也就是說,有很多類和成員變數在我們編譯的時是不知道的,而在執行時,我們所編寫的程式碼會轉換成完整的確定的程式碼執行。因此,我們還需要一個執行時系統(Runtime system)來處理編譯後的程式碼。例如OC中的類物件,都是在程式的執行期才知道這個物件是哪個類的物件,該類具有哪些特徵,等等。我們執行的函式例如:[foo doSomeThing],其實是通過函式objc_msgSend(foo, doSomeThing)來呼叫的,編譯器將會在foo類的方法樹中進行查詢。
  • 具體的應用:JSON轉Model,Model轉資料庫語言,方法交換Method Swizzing,訊息轉發,等等。

19.MJExtension, MJRefresh, SDWebImage的實現原理

  • MJExtension:通過執行時,拿到Model對應的PropertyName,然後通過KVC,將字典中的值傳入Model。
  • MJRefresh:所有的View繼承於父類MJRefreshComponent,通過監聽SCrollView的contentOffset來判斷Refresh狀態,從而觸發各種狀態的函式方法。
  • SDWebImage:SDWebImageManager拿到需要請求的URL,會進行三級快取的查詢:NSCache, Memery磁碟,伺服器。SDWebImageDownLoader會下載圖片對應的Data,開啟CGRefImage上下文進行圖片非同步渲染繪製得到UIImage然後傳入UIImageView。

  1. NSTimer計時器是準確的嗎,為什麼?
  • NSTimer計時器不是準確的。
  • 原因:定時器被新增在主執行緒中,由於定時器在一個RunLoop中被檢測一次,所以如果在這一次的RunLoop中做了耗時的操作,當前RunLoop持續的時間超過了定時器的間隔時間,那麼下一次定時就被延後了。

  1. 類的分類和類的擴充套件的區別,類的分類的實現原理。
  • 類的分類可以動態新增方法(執行時),類的擴充套件可以新增更多的屬性變數(編譯期)。  
  • 類的分類的實現原理:在執行時過程中,本類的方法載入完畢之後,會查詢是否有類的分類,如果有類的分類,就會再去載入類的分類的方法,把這些方法全部儲存到objc_class結構體的methodLists陣列中。注意,這裡的類的分類的方法是插入到陣列的第一個元素位置的,也就是說類的分類的方法會覆蓋本類的同名方法。如果有多個類的分類都包含同名函式,那麼最後一個被載入進compile sources的類的分類檔案中的方法將會覆蓋其他的同名方法。

  1. iOS動態關聯屬性(objc_setAssociatedObject,objc_getAssocicatedObject)的實現原理
  • AssociationsManager 是頂級的物件,維護了一個從spinlock_t 鎖到AssociationsHashMap雜湊表的單例鍵值對對映;
  • AssociationsHashMap是一個無序的雜湊表,維護了從物件地址到 ObjectAssociationMap的對映;
  • ObjectAssociationMap 是一個 C 中的 map ,維護了從 key 到 ObjcAssociation 的對映,即關聯記錄;
  • ObjcAssociation是一個C的類,表示一個具體的關聯結構,主要包括兩個例項變數,_policy 表示關聯策略,_value 表示關聯物件。

  1. Masonry的抗壓縮屬性和抗拉伸屬性
  • 一般情況下,用Masonry設定兩個平行的自適應Label(左面為Label1,右面為Label2),那麼效果圖應該如下:
    iOS面試珠璣
  • 但是我想達到這樣的效果,應該怎麼設定呢?
    iOS面試珠璣
  • 可以設定抗拉伸屬性優先順序,程式碼如下:
[label1 setContentHuggingPriority:UILayoutPriorityRequired
                          forAxis:UILayoutConstraintAxisHorizontal];
[label2 setContentHuggingPriority:UILayoutPriorityDefaultLow
                          forAxis:UILayoutConstraintAxisHorizontal];
複製程式碼
  • 當文字過多的時候,一般效果如下:
    iOS面試珠璣
  • 那麼我想實現這樣的效果,應該怎麼辦呢?
    iOS面試珠璣
  • 可以設定抗壓縮屬性,程式碼如下:
[label1 setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                        forAxis:UILayoutConstraintAxisHorizontal];
[label2 setContentCompressionResistancePriority:UILayoutPriorityRequired
                                        forAxis:UILayoutConstraintAxisHorizontal];
複製程式碼
  1. 加密的種類,對稱加密和非對稱加密
  • 加密種類:
  • 對稱加密演算法:MD5,DES,AES。
  • 非對稱加密演算法:RSA等加密方式。
  • 對稱加密:對稱加密是最快速、最簡單的一種加密方式,加密與解密用的是同樣的金鑰,這種方法在密碼學中叫做對稱加密演算法。
  • 非對稱加密:非對稱加密為資料的加密與解密提供了一個非常安全的方法,它使用了一對金鑰,公鑰和私鑰。私鑰只能由一方安全保管,不能外洩,而公鑰則可以發給任何請求它的人。非對稱加密使用這對金鑰中的一個進行加密,而解密則需要另一個金鑰。

  1. 解釋一下七層網路結構,三次握手協議和四次揮手協議
  • 七層網路協議:

    由低到高:物理層、資料鏈路層、網路層、傳輸層、表示層、會話層、應用層。
  • 三次握手協議:

    第一次握手:建立連線時,客戶端傳送syn包(syn=j)到伺服器,並進入SYN_SENT狀態,等待伺服器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
    第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也傳送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;
    第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED(TCP連線成功)狀態,完成三次握手。
  • 四次揮手協議:

    1.第一次揮手:主機1(可以使客戶端,也可以是伺服器端),設定Sequence Number和Acknowledgment Number,向主機2傳送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有資料要傳送給主機2了;
    2.第二次揮手:主機2收到了主機1傳送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number為Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我也沒有資料要傳送了,可以進行關閉連線了;
    3.第三次揮手:主機2向主機1傳送FIN報文段,請求關閉連線,同時主機2進入CLOSE_WAIT狀態;
    4.第四次揮手:主機1收到主機2傳送的FIN報文段,向主機2傳送ACK報文段,然後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段以後,就關閉連線;此時,主機1等待2MSL後依然沒有收到回覆,則證明Server端已正常關閉,那好,主機1也可以關閉連線了。

  1. http和https的區別
  • https在http的基礎上增加了SSL資料傳輸安全性認證層。具體關係如下圖:
    iOS面試珠璣
  • https協議需要到ca申請證書。http是超文字傳輸協議,資訊是明文傳輸,https 則是具有安全性的ssl加密傳輸協議。http和https使用的是完全不同的連線方式用的埠也不一樣,前者是80,後者是443。http的連線很簡單,是無狀態的。HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議。

  1. https雙向驗證原理
  • 1.瀏覽器將自己支援的一套加密規則傳送給網站。
  • 2.網站從中選出一組加密演算法與HASH演算法,並將自己的身份資訊以證書的形式發回給瀏覽器。證書裡面包含了網站地址,加密公鑰,以及證書的頒發機構等資訊。
  • 3.瀏覽器獲得網站證書之後瀏覽器要做以下工作:
    a) 驗證證書的合法性(頒發證書的機構是否合法,證書中包含的網站地址是否與正在訪問的地址一致等),如果證書受信任,則瀏覽器欄裡面會顯示一個小鎖頭,否則會給出證書不受信的提示。
    b) 如果證書受信任,或者是使用者接受了不受信的證書,瀏覽器會生成一串隨機數的密碼,並用證書中提供的公鑰加密。
    c) 使用約定好的HASH演算法計算握手訊息,並使用生成的隨機數對訊息進行加密,最後將之前生成的所有資訊傳送給網站。
  • 4.網站接收瀏覽器發來的資料之後要做以下的操作:
    a) 使用自己的私鑰將資訊解密取出密碼,使用密碼解密瀏覽器發來的握手訊息,並驗證HASH是否與瀏覽器發來的一致。
    b) 使用密碼加密一段握手訊息,傳送給瀏覽器。
  • 5.瀏覽器解密並計算握手訊息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,之後所有的通訊資料將由之前瀏覽器生成的隨機密碼並利用對稱加密演算法進行加密。
  • 具體的流程見下圖:

iOS面試珠璣


  1. HTTP常用的頭部欄位,常見的返回狀態碼和意義
  • 常見的頭部欄位:

    host頭域
    Host頭域指定請求資源的Intenet主機和埠號,必須表示請求url的原始伺服器或閘道器的位置。HTTP/1.1請求必須包含主機頭域,否則系統會以400狀態碼返回。
    Referer頭域
    Referer頭域允許客戶端指定請求uri的源資源地址,這可以允許伺服器生成回退連結串列,可用來登陸、優化cache等。他也允許廢除的或錯誤的連線由於維護的目的被追蹤。如果請求的uri沒有自己的uri地址,Referer不能被髮送。如果指定的是部分uri地址,則此地址應該是一個相對地址。
    User-Agent頭域
    User-Agent頭域的內容包含發出請求的使用者資訊,User Agent也簡稱UA。用較為普通的一點來說,是一種向訪問網站提供你所使用的瀏覽器型別、作業系統及版本、CPU型別、瀏覽器渲染引擎、瀏覽器語言、瀏覽器外掛等資訊的標識。
    Cache-Control頭域
    Cache-Control指定請求和響應遵循的快取機制。在請求訊息或響應訊息中設定Cache-Control並不會修改另一個訊息處理過程中的快取處理過程。請求時的快取指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,響應訊息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
    Date頭域
    Date頭域表示訊息傳送的時間,時間的描述格式由rfc822定義。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的時間表示世界標準時,換算成本地時間,需要知道使用者所在的時區。
  • 返回的狀態碼:

    200(成功) 伺服器已成功處理了請求。通常,這表示伺服器提供了請求的網頁。
    201(已建立) 請求成功且伺服器已建立了新的資源。
    202(已接受) 伺服器已接受了請求,但尚未對其進行處理。
    203(非授權資訊) 伺服器已成功處理了請求,但返回了可能來自另一來源的資訊。
    204(無內容) 伺服器成功處理了請求,但未返回任何內容。
    205(重置內容) 伺服器成功處理了請求,但未返回任何內容。與 204 響應不同,此響應要求請求者重置文件檢視(例如清除表單內容以輸入新內容)。
    206(部分內容) 伺服器成功處理了部分 GET 請求。
    300(多種選擇) 伺服器根據請求可執行多種操作。伺服器可根據請求者 來選擇一項操作,或提供操作列表供其選擇。
    301(永久移動) 請求的網頁已被永久移動到新位置。伺服器返回此響應時,會自動將請求者轉到新位置。您應使用此程式碼通知搜尋引擎蜘蛛網頁或網站已被永久移動到新位置。
    302(臨時移動) 伺服器目前正從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以後的請求。會自動將請求者轉到不同的位置。但由於搜尋引擎會繼續抓取原有位置並將其編入索引,因此您不應使用此程式碼來告訴搜尋引擎頁面或網站已被移動。
    303(檢視其他位置) 當請求者應對不同的位置進行單獨的 GET 請求以檢索響應時,伺服器會返回此程式碼。對於除 HEAD ##### 304(未修改) 自從上次請求後,請求的網頁未被修改過。伺服器返回此響應時,不會返回網頁內容。如果網頁自請求者上次請求後再也沒有更改過,您應當將伺服器配置為返回此響應。由於伺服器可以告訴 搜尋引擎自從上次抓取後網頁沒有更改過,因此可節省頻寬和開銷。
    305(使用代理) 請求者只能使用代理訪問請求的網頁。如果伺服器返回此響應,那麼,伺服器還會指明請求者應當使用的代理。
    307(臨時重定向) 伺服器目前正從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以後的請求。會自動將請求者轉到不同的位置。但由於搜尋引擎會繼續抓取原有位置並將其編入索引,因此您不應使用此程式碼來告訴搜尋引擎某個頁面或網站已被移動。
    400(錯誤請求) 伺服器不理解請求的語法。
    401(身份驗證錯誤) 此頁要求授權。您可能不希望將此網頁納入索引。
    403(禁止) 伺服器拒絕請求。
    404(未找到) 伺服器找不到請求的網頁。例如,對於伺服器上不存在的網頁經常會返回此程式碼。例如:www.0631abc.com/20100aaaa,就…
    405(方法禁用) 禁用請求中指定的方法。
    406(不接受) 無法使用請求的內容特性響應請求的網頁。
    407(需要代理授權) 此狀態碼與 401 類似,但指定請求者必須授權使用代理。如果伺服器返回此響應,還表示請求者應當使用代理。
    408(請求超時) 伺服器等候請求時發生超時。
    409(衝突) 伺服器在完成請求時發生衝突。伺服器必須在響應中包含有關衝突的資訊。伺服器在響應與前一個請求相沖突的 PUT 請求時可能會返回此程式碼,以及兩個請求的差異列表。
    410(已刪除) 請求的資源永久刪除後,伺服器返回此響應。該程式碼與 404(未找到)程式碼相似,但在資源以前存在而現在不存在的情況下,有時會用來替代 404 程式碼。如果資源已永久刪除,您應當使用 301 指定資源的新位置。
    411(需要有效長度) 伺服器不接受不含有效內容長度標頭欄位的請求。
    412(未滿足前提條件) 伺服器未滿足請求者在請求中設定的其中一個前提條件。
    413(請求實體過大) 伺服器無法處理請求,因為請求實體過大,超出伺服器的處理能力。
    414(請求的 URI 過長) 請求的 URI(通常為網址)過長,伺服器無法處理。
    415(不支援的媒體型別) 請求的格式不受請求頁面的支援。
    416(請求範圍不符合要求) 如果頁面無法提供請求的範圍,則伺服器會返回此狀態碼。
    417(未滿足期望值) 伺服器未滿足"期望"請求標頭欄位的要求。
    500(伺服器內部錯誤) 伺服器遇到錯誤,無法完成請求。
    501(尚未實施) 伺服器不具備完成請求的功能。例如,當伺服器無法識別請求方法時,伺服器可能會返回此程式碼。
    502(錯誤閘道器) 伺服器作為閘道器或代理,從上游伺服器收到了無效的響應。
    503(服務不可用) 目前無法使用伺服器(由於超載或進行停機維護)。通常,這只是一種暫時的狀態。
    504(閘道器超時) 伺服器作為閘道器或代理,未及時從上游伺服器接收請求。
    505(HTTP 版本不受支援) 伺服器不支援請求中所使用的 HTTP 協議版本。

  1. @class和import以及include的區別
  • import會引入整個.h標頭檔案。
  • @class只是告訴編譯器該類中可以使用這個class類名。
  • include和import的作用類似,但是可能造成重複引用的問題,一般用判斷巨集定義是否存在的方式來防止迴圈引用。

  1. weak物件的管理方式
  • weak是弱引用,所引用物件的計數器不會加一,並在引用物件被釋放的時候自動被設定為nil。runtime維護了一個weak表,用於儲存指向某個物件的所有weak指標。weak表其實是一個hash(雜湊)表,Key是所指物件的地址,Value是weak指標的地址(這個地址的值是所指物件的地址)陣列。

  • 1、初始化時:runtime會呼叫objc_initWeak函式,初始化一個新的weak指標指向物件的地址。

  • 2、新增引用時:objc_initWeak函式會呼叫 objc_storeWeak() 函式, objc_storeWeak() 的作用是更新指標指向,建立對應的弱引用表。

  • 3、釋放時,呼叫clearDeallocating函式。clearDeallocating函式首先根據物件地址獲取所有weak指標地址的陣列,然後遍歷這個陣列把其中的資料設為nil,最後把這個entry從weak表中刪除,最後清理物件的記錄。


  1. iOS的retain和release的操作是在編譯期還是執行時進行的
  • retain和release是在編譯期由編譯器自動生成的程式碼,例如:
- (void) setUserName:(UITextField *)userName { 
    [_userName release]; 
    _userName = [userName retain]; 
}
複製程式碼

  1. +(void)load方法和+(void)initial方法的異同
  • +(void)load方法:
  • 對於加入執行期系統的類及分類,必定會呼叫此方法,且僅呼叫一次。 iOS會在應用程式啟動的時候呼叫load方法,在main函式之前呼叫 執行子類的load方法前,會先執行所有超類的load方法,順序為父類->子類->分類 在load方法中使用其他類是不安全的,因為會呼叫其他類的load方法,而如果關係複雜的話,就無法判斷出各個類的載入順序,類只有初始化完成後,類例項才能進行正常使用 load 方法不遵從繼承規則,如果類本身沒有實現load方法,那麼系統就不會呼叫,不管父類有沒有實現(跟下文的initialize有明顯區別) 儘可能的精簡load方法,因為整個應用程式在執行load方法時會阻塞,即,程式會阻塞直到所有類的load方法執行完畢,才會繼續 load 方法中最常用的就是方法交換method swizzling。
  • +(void)initial方法:
  • 在首次使用該類之前由執行期系統呼叫,且僅呼叫一次 惰性呼叫,只有當程式使用相關類時,才會呼叫 執行期系統會確保initialize方法是線上程安全的環境中執行,即,只有執行initialize的那個執行緒可以操作類或類例項。其他執行緒都要先阻塞,等待initialize執行完。如果類未實現initialize方法,而其超類實現了,那麼會執行超類的實現程式碼,而且會執行兩次。initialize方法是執行緒安全的,可以用來設定內部資料,比如,某個全域性狀態,如陣列、字典等無法在編譯期初始化,可以放在initialize裡面。

  1. UIViewController的生命週期方法呼叫順序
  • 當一個檢視控制器被建立,並在螢幕上現實的時候。程式碼的執行順序:

    1.alloc       建立物件,分配空間。
    2.init       初始化物件,初始化資料。
    3.loadView    從nib載入檢視,通常這一步不需要去幹涉。除非你沒有使用xib檔案建立檢視
    4.viewDidLoad   載入完成,可以進行自定義資料以及動態的建立其他空間。
    5.viewWillAppear  檢視將出現在螢幕之前。
    6.viewDidAppear  檢視在螢幕上渲染完成。
  • 當一個檢視被移除螢幕並且銷燬的時候執行順序:

    1.viewWillDisappear  檢視被移除之前。
    2.viewDidDisappear   檢視被移除之後。
    3.dealloc        銷燬檢視。

  1. iOS中各類控制元件的繼承樹關係
  • 一張圖告訴你所有的繼承關係:
    iOS面試珠璣

  1. 如何化解NSTimer的迴圈引用關係
  • 首先要理解NSTimer為什麼會引起迴圈引用:NSTimer和使用Timer的ViewController相互持有。
  • 解決辦法有兩個:
    1. 在ViewContoller的viewWillDisappear生命週期中登出Timer。
    2. 引入第三方NSObject(如HWWeakTimer)管理和持有Timer,讓Timer持有第三方的成員變數。這樣就打破了互相引用的迴圈關係。

  1. 怎樣管理第三方SDK,CocoaPods和Carthage的異同
  • iOS中一般使用CocoaPods和Carthage來管理第三方SDK。

  • 兩者的比較:

    1. Carthage只支援iOS 8及以上版本使用。
    2. CocoaPods預設會自動建立並更新你的應用程式和所有依賴的Xcode workspace。Carthage使用xcodebuild來編譯框架的二進位制檔案,但如何整合它們將交由使用者自己判斷。CocoaPods的方法更易於使用,但Carthage更靈活並且是非侵入性的。
    我們建立Carthage的原因是想要一種儘可能簡單的工具——一個只關心本職工作的依賴管理器,而不是取代部分Xcode的功能,或者需要讓框架作者做一些額外的工作。CocoaPods提供的一些特性很棒,但由於附加的複雜性,它們將不會被包含在Carthage當中。

  1. -(BOOL)isKindOfClass和-(BOOL)isMemberOfClass的區別
  • -(BOOL) isKindOfClass: classObj 判斷是否是這個類或者這個類的子類的例項

  • -(BOOL) isMemberOfClass: classObj 判斷是否是這個類的例項


  1. 資料持久化的幾種方式和對應的應用場景
  • plist檔案(屬性列表):即直接拖拽plist檔案到程式目錄當中。由NSBundle獲取本地plist資源。儲存一些本地的,且不會改變的資料到程式當中。
  • preference(偏好設定):即NSUserDefaults,儲存一些小型資料,設定引數,開關屬性等等。
  • NSKeyedArchiver(歸檔):儲存一些不涉及增刪改查的字典陣列或者NSObject等,儲存的物件一定要遵循NSCoder和NSDecoder協議。
  • SQLite 3:儲存一些涉及增刪改查的欄位資料。
  • CoreData:效率比較高,儲存一些涉及增刪改查的且體積非常大的資料。

  1. 如何實現一個完整的單例
  • 實現了單例的初始化之後,一定要重寫+(id) allocWithZone:(struct _NSZone *)zone,-(id) copyWithZone:(NSZone *)zone,-(id) mutablecopyWithZone:(NSZone *)zone這三個方法,程式碼如下:
#import "Singleton.h"
@interface Singleton()<NSCopying,NSMutableCopying>
@end
 
@implementation Singleton
 
static Singleton* _instance = nil;
 
+(instancetype) shareInstance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
        //不是使用alloc方法,而是呼叫[[super allocWithZone:NULL] init] 
        //已經過載allocWithZone基本的物件分配方法,所以要借用父類(NSObject)的功能來幫助出處理底層記憶體分配的雜物
    }) ;
     
    return _instance ;
}
 
+(id) allocWithZone:(struct _NSZone *)zone
{
    return [Singleton shareInstance] ;
}
 
-(id) copyWithZone:(NSZone *)zone
{
    return [Singleton shareInstance] ;//return _instance;
}
 
-(id) mutablecopyWithZone:(NSZone *)zone
{
    return [Singleton shareInstance] ;
}
@end
複製程式碼

  1. iOS的系統單例有哪些?
  • [UIScreen mainScreen] (應用程式視窗)

  • [UIDevice currentDevice] (當前裝置)

  • [UIApplication sharedApplication] (應用程式例項)

  • [NSNotificationCenter defaultCenter] (訊息中心):

  • [NSFileManager defaultManager] (檔案管理):

  • [NSUserDefaults standardUserDefaults] (應用程式設定):

  • [NSURLCache sharedURLCache] (請求快取):

  • [NSHTTPCookieStorage sharedHTTPCookieStorage] (應用程式cookies池)


  1. APP啟動主要流程
  • APP啟動主要流程: 點選icon -> 載入動態連結庫等 -> 映像檔案載入imageLoader -> runtime -> load -> main -> delegate。

  1. iOS的沙盒機制
  • 出於安全考慮,iPhone對於安裝在上面的應用程式有所限制,這個限制就是應用程式只能在為該改程式建立的檔案系統中讀取檔案,不可以去其它地方訪問,此區域被成為沙盒,所以所有的非程式碼檔案都要儲存在此,例如影像,圖示,聲音,映像,屬性列表,文字檔案等。總體來說沙盒就是一種獨立、安全、封閉的空間。沙盒(sandbox)的核心內容是:sandbox對應用程式執行各種操作的許可權限制。

  • 沙盒的特點:

    1. 每個應用程式都有自己的儲存空間。
    2. 每個應用程式都不可以翻過自己的圍牆去訪問別的儲存空間的內容。(已經越獄的除外)
    3. 在訪問別人沙盒內的資料時需要訪問許可權。
  • 應用程式沙盒目錄下有三個資料夾Documents、Library(下面有Caches和Preferences目錄)、tmp。

    Documents:儲存應用執行時生成的需要持久化的資料iTunes會自動備份該目錄。蘋果建議將在應用程式中瀏覽到的檔案資料儲存在該目錄下。
    Library/Caches:一般儲存的是快取檔案,例如圖片視訊等,此目錄下的檔案不會再應用程式退出時刪除,在手機備份的時候,iTunes不會備份該目錄。
    Library/Preferences:儲存應用程式的所有偏好設定iOS的Settings(設定),我們不應該直接在這裡建立檔案,而是需要通過NSUserDefault這個類來訪問應用程式的偏好設定。iTunes會自動備份該檔案目錄下的內容。
    tmp:臨時檔案目錄,在程式重新執行的時候,和開機的時候,會清空tmp資料夾。

以上是Fabric自己總結的一些面試心經,有興趣的朋友可以加我微信共同討論:justlikeitRobert。各位老鐵看了我這篇文章之後,如果找到了滿意的工作,別忘了和Fabric一起分享成功喜悅,一分也是愛,哈哈!

iOS面試珠璣

相關文章