iOS 美團面試整理

1, HTTP和HTTPS 區(qū)別,HTTPS證書驗證原理

區(qū)別

1,HTTP 明文傳輸,數(shù)據(jù)都是未加密的,安全性較差,HTTPS(SSL+HTTP) 數(shù)據(jù)傳輸過程是加密的,安全性較好。
2,使用 HTTPS 協(xié)議需要到 CA(Certificate Authority,數(shù)字證書認(rèn)證機構(gòu)) 申請證書,一般免費證書較少,因而需要一定費用。證書頒發(fā)機構(gòu)如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
3,HTTP 頁面響應(yīng)速度比 HTTPS 快,主要是因為 HTTP 使用 TCP 三次握手建立連接,客戶端和服務(wù)器需要交換 3 個包,而 HTTPS除了 TCP 的三個包,還要加上 ssl 握手需要的 9 個包,所以一共是 12 個包。
4,HTTP 和 HTTPS 使用的是完全不同的連接方式,用的端口也不一樣,前者是 80,后者是 443。
HTTPS 其實就是建構(gòu)在 SSL/TLS 之上的 HTTP 協(xié)議,所以,要比較 HTTPS 比 HTTP 要更耗費服務(wù)器資源

HTTPS證書驗證原理

1、客戶端發(fā)起 HTTPS 請求
這個沒什么好說的,就是用戶在瀏覽器里輸入一個 https 網(wǎng)址,然后連接到 server 的 443 端口。
2、服務(wù)端的配置
采用 HTTPS 協(xié)議的服務(wù)器必須要有一套數(shù)字證書,可以自己制作,也可以向組織申請,區(qū)別就是自己頒發(fā)的證書需要客戶端驗證通過,才可以繼續(xù)訪問,而使用受信任的公司申請的證書則不會彈出提示頁面(startssl 就是個不錯的選擇,有 1 年的免費服務(wù))。
這套證書其實就是一對公鑰和私鑰,如果對公鑰和私鑰不太理解,可以想象成一把鑰匙和一個鎖頭,只是全世界只有你一個人有這把鑰匙,你可以把鎖頭給別人,別人可以用這個鎖把重要的東西鎖起來,然后發(fā)給你,因為只有你一個人有這把鑰匙,所以只有你才能看到被這把鎖鎖起來的東西。
3、傳送證書
這個證書其實就是公鑰,只是包含了很多信息,如證書的頒發(fā)機構(gòu),過期時間等等。
4、客戶端解析證書
這部分工作是有客戶端的TLS來完成的,首先會驗證公鑰是否有效,比如頒發(fā)機構(gòu),過期時間等等,如果發(fā)現(xiàn)異常,則會彈出一個警告框,提示證書存在問題。
如果證書沒有問題,那么就生成一個隨機值,然后用證書對該隨機值進行加密,就好像上面說的,把隨機值用鎖頭鎖起來,這樣除非有鑰匙,不然看不到被鎖住的內(nèi)容。
5、傳送加密信息
這部分傳送的是用證書加密后的隨機值,目的就是讓服務(wù)端得到這個隨機值,以后客戶端和服務(wù)端的通信就可以通過這個隨機值來進行加密解密了。
6、服務(wù)端解密信息
服務(wù)端用私鑰解密后,得到了客戶端傳過來的隨機值(私鑰),然后把內(nèi)容通過該值進行對稱加密,所謂對稱加密就是,將信息和私鑰通過某種算法混合在一起,這樣除非知道私鑰,不然無法獲取內(nèi)容,而正好客戶端和服務(wù)端都知道這個私鑰,所以只要加密算法夠彪悍,私鑰夠復(fù)雜,數(shù)據(jù)就夠安全。
7、傳輸加密后的信息
這部分信息是服務(wù)段用私鑰加密后的信息,可以在客戶端被還原。
8、客戶端解密信息
客戶端用之前生成的私鑰解密服務(wù)段傳過來的信息,于是獲取了解密后的內(nèi)容,整個過程第三方即使監(jiān)聽到了數(shù)據(jù),也束手無策。

參考文章HTTP和HTTPS 區(qū)別 ,HTTP/HTTPS

2,常用的加密方式

常用加密算法
編碼方式 : Base64 Base58
哈希(散列)函數(shù) : MD5(消息摘要算法) SHA1 SHA256 SHA512
對稱加密算法 : DES AES
非對稱加密算法 : RSA(公鑰、私鑰) ECC

參考文章常用加密算法

3,isKindOfClass、isMemberOfClass區(qū)別,底層代碼實現(xiàn)差別

isKindOfClass 可判斷是否處于繼承鏈上的類

  + (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

isMemberOfClass 只能判斷本身的類是否一致

   + (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

區(qū)別是isKindOfClass 是for循環(huán)沿著繼承鏈,一級一級向上查找,isMemberOfClass只是判斷object_getClass((id)self),當(dāng)前objc是一個類對象,那么獲取的類就是當(dāng)前類的元類,如果objc是一個實例對象,那么獲取的就是當(dāng)前的類。

4,category,extension區(qū)別,從底層分析category為什么不能添加實例變量

Category是運行時決定生效的,Extension是編譯時就決定生效的
Category可以為系統(tǒng)類添加分類,Extension不能
Category是有聲明和實現(xiàn),Extension直接寫在宿主.m文件,只有聲明
Category只能擴充方法,不能擴充成員變量和屬性
如果Category聲明了聲明了一個屬性,那么Category只會生成這個屬性的set,get方法的聲明,也就不是會實現(xiàn)
Category底層結(jié)構(gòu)體 category_t 只有方法緩存,方法列表,協(xié)議列表以及屬性列表,并沒有存放成員變量的ivar,所以不支持添加成員變量,分析可參考這個文章 深入理解Category

5,objc_class , isa_t結(jié)構(gòu)

objc_class
/// OC 中對象的結(jié)構(gòu)體
struct objc_class : objc_object {
    Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    // bits用于存儲類名、類版本號、方法列表、協(xié)議列表等信息,替代了Objective-C1.0中methodLists、protocols等成員變量。
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    // class_rw_t 表示 class 是readwrite的 class_ro_t 表示class是readonly的
    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
  // ....省略一些方法
}
// class_ro_t存放的是編譯期間就確定的;而class_rw_t是在runtime時才確定。class_ro_t是 class_rw_t中的一個屬性,所以可以說class_rw_t是class_ro_t的超集
struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    // 只讀class 結(jié)構(gòu)體
    const class_ro_t *ro;
    //方法列表
    method_array_t methods;
    //屬性列表
    property_array_t properties;
    //協(xié)議列表
    protocol_array_t protocols;
    Class firstSubclass;
    Class nextSiblingClass;
}
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    uint32_t reserved;

    const uint8_t * ivarLayout;

    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};
isa_t
union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { } 
    Class cls;
    uintptr_t bits; 

#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t nonpointer        : 1; // 代表是否開啟NONPOINTER isa指針優(yōu)化,之前的版本又叫index的其實一個意思蘋果后來給這類優(yōu)化方案起了名字NONPOINTER
        uintptr_t has_assoc         : 1; // 對象是否含有關(guān)聯(lián)引用
        uintptr_t has_cxx_dtor      : 1; // 對象是否含有 C++ 或者 Objc 的析構(gòu)器
        uintptr_t shiftcls          : 33; // 類的指針arm64下3bits,x86_64下44bits
        uintptr_t magic             : 6; // 判斷對象是否初始化完成 arm下0x16 ,x86_64下 0x3b
        uintptr_t weakly_referenced : 1; //是否為弱引用的對象
        uintptr_t deallocating      : 1; //對象是否正在執(zhí)行析構(gòu)函數(shù)(是否在釋放內(nèi)存)
        uintptr_t has_sidetable_rc  : 1; // 判斷是否需要用sidetable去處理引用計數(shù),(extra_rc的大小影響到這個變量)
        uintptr_t extra_rc          : 19; //  存儲該對象的引用計數(shù)值減一后的結(jié)果
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    }; 
}; 

參考文章 objc_classisa_t

6,runloop相關(guān)內(nèi)容

7,自動釋放池相關(guān)內(nèi)容

8,dealloc釋放過程

對象內(nèi)存銷毀時刻表
1,調(diào)用-release 對象的引用計數(shù)為0
??a,對象正在被銷毀,生命周期結(jié)束
??b,不在有新的 __weak 弱引用,否則將指向nil
??調(diào)用[self dealloc]
2,子類調(diào)用 -dealloc
??a,繼承鏈最底層的子類調(diào)用 -dealloc
??b,MRC下則會手動釋放實例變量
??c,繼承關(guān)系中沒一層父類調(diào)用 -dealloc
3,NSObject 調(diào)用 -dealloc
??調(diào)用object_dispose()方法
4,調(diào)用object_dispose()
??為C++對象們(iVars)調(diào)用 destructors
??為ARC對象們(iVars)調(diào)用 -release
??解除所有runtime 方法關(guān)聯(lián)的對象
??解除所有 __weak 引用
??調(diào)用 free()

48F6BDE411B4BE47B2244486A72648A0.jpg

參考文章Dealloc還需要注意什么

9,如何動態(tài)替換實例對象的某個方法

  1. 可參考KVO的實現(xiàn)(動態(tài)創(chuàng)建一個子類,利用isa指針交換,在子類重寫該方法,進行消息轉(zhuǎn)發(fā))
  2. 使用class_replaceMethod/class_addMethod函數(shù)在運行時對函數(shù)進行動態(tài)替換或增加新函數(shù)
  3. 直接用_objc_msgForward 走消息轉(zhuǎn)發(fā)流程,然后在快轉(zhuǎn)過程中進行替換

參考文章如何為一個實例動態(tài)替換方法

10,重排鏈表

題目描述

解題思路
1、通過雙指針,找到鏈表中間節(jié)點,然后將之前鏈表分為前后兩個鏈表(通過慢指針斷開))
2、反轉(zhuǎn)后鏈表
3、將前后兩個鏈表進行合并,先前鏈表內(nèi)容,然后再是后鏈表內(nèi)容
4、如果還有兩邊不為空,則直接添加單最終鏈表的尾部

代碼實現(xiàn)
 /**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class Solution {
    func reorderList(_ head: ListNode?) {
        if head == nil || head?.next == nil {
            return
        }
        let node: ListNode? = head
        var slow: ListNode? = head
        var fast: ListNode? = head?.next
        while fast != nil && fast?.next != nil {
            slow = slow?.next
            fast = fast?.next?.next
        }
        let halfNode: ListNode? = slow?.next
        slow?.next = nil
        let reverseNode: ListNode? = self.reverseReorderList(halfNode)
        self.mergeNodeList(node, reverseNode)
    }
    
    //反轉(zhuǎn)鏈表
    func reverseReorderList(_ head: ListNode?) -> ListNode?{
        if head == nil {
            return nil
        }
        let curNode: ListNode? = head
        var nextNode: ListNode? = head
        var frontNode: ListNode? = head
        while curNode?.next != nil {
            nextNode = curNode?.next
            curNode?.next = nextNode?.next
            nextNode?.next = frontNode
            frontNode = nextNode
        }
        return frontNode
    }
    
    func mergeNodeList(_ n1: ListNode?, _ n2: ListNode?){
        var beforeNode: ListNode? = n1 //i
        var afterNode: ListNode? = n2   //j
        var resultNode: ListNode? = ListNode.init(0);
        while beforeNode != nil && afterNode != nil{
            resultNode?.next = beforeNode
            resultNode = resultNode?.next
            beforeNode = beforeNode?.next
            
            resultNode?.next = afterNode
            resultNode = resultNode?.next
            afterNode = afterNode?.next
        }
        resultNode?.next = beforeNode == nil ? afterNode : beforeNode
    }
}

11,有序數(shù)組中刪除重復(fù)項

func removeDuplicates(_ nums: inout [Int]) -> [Int] {
        if nums.count == 0 {
            return 0
        }
        var left = 0
        var right = 1
        while right < nums.count {
            if nums[left] != nums[right] {
                left += 1
                nums[left] = nums[right]
            }
            right += 1
        }
        return nums
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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