iOS 美团面试整理

1, HTTP和HTTPS 区别,HTTPS证书验证原理

区别

1,HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
2,使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构) 申请证书,一般免费证书较少,因而需要一定费用。证书颁发机构如:Symantec、Comodo、GoDaddy 和 GlobalSign 等。
3,HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
4,HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源

HTTPS证书验证原理

1、客户端发起 HTTPS 请求
这个没什么好说的,就是用户在浏览器里输入一个 https 网址,然后连接到 server 的 443 端口。
2、服务端的配置
采用 HTTPS 协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请,区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面(startssl 就是个不错的选择,有 1 年的免费服务)。
这套证书其实就是一对公钥和私钥,如果对公钥和私钥不太理解,可以想象成一把钥匙和一个锁头,只是全世界只有你一个人有这把钥匙,你可以把锁头给别人,别人可以用这个锁把重要的东西锁起来,然后发给你,因为只有你一个人有这把钥匙,所以只有你才能看到被这把锁锁起来的东西。
3、传送证书
这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间等等。
4、客户端解析证书
这部分工作是有客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。
如果证书没有问题,那么就生成一个随机值,然后用证书对该随机值进行加密,就好像上面说的,把随机值用锁头锁起来,这样除非有钥匙,不然看不到被锁住的内容。
5、传送加密信息
这部分传送的是用证书加密后的随机值,目的就是让服务端得到这个随机值,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。
6、服务端解密信息
服务端用私钥解密后,得到了客户端传过来的随机值(私钥),然后把内容通过该值进行对称加密,所谓对称加密就是,将信息和私钥通过某种算法混合在一起,这样除非知道私钥,不然无法获取内容,而正好客户端和服务端都知道这个私钥,所以只要加密算法够彪悍,私钥够复杂,数据就够安全。
7、传输加密后的信息
这部分信息是服务段用私钥加密后的信息,可以在客户端被还原。
8、客户端解密信息
客户端用之前生成的私钥解密服务段传过来的信息,于是获取了解密后的内容,整个过程第三方即使监听到了数据,也束手无策。

参考文章HTTP和HTTPS 区别 ,HTTP/HTTPS

2,常用的加密方式

常用加密算法
编码方式 : Base64 Base58
哈希(散列)函数 : MD5(消息摘要算法) SHA1 SHA256 SHA512
对称加密算法 : DES AES
非对称加密算法 : RSA(公钥、私钥) ECC

参考文章常用加密算法

3,isKindOfClass、isMemberOfClass区别,底层代码实现差别

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;
}

区别是isKindOfClass 是for循环沿着继承链,一级一级向上查找,isMemberOfClass只是判断object_getClass((id)self),当前objc是一个类对象,那么获取的类就是当前类的元类,如果objc是一个实例对象,那么获取的就是当前的类。

4,category,extension区别,从底层分析category为什么不能添加实例变量

Category是运行时决定生效的,Extension是编译时就决定生效的
Category可以为系统类添加分类,Extension不能
Category是有声明和实现,Extension直接写在宿主.m文件,只有声明
Category只能扩充方法,不能扩充成员变量和属性
如果Category声明了声明了一个属性,那么Category只会生成这个属性的set,get方法的声明,也就不是会实现
Category底层结构体 category_t 只有方法缓存,方法列表,协议列表以及属性列表,并没有存放成员变量的ivar,所以不支持添加成员变量,分析可参考这个文章 深入理解Category

5,objc_class , isa_t结构

objc_class
/// OC 中对象的结构体
struct objc_class : objc_object {
    Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    // bits用于存储类名、类版本号、方法列表、协议列表等信息,替代了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 结构体
    const class_ro_t *ro;
    //方法列表
    method_array_t methods;
    //属性列表
    property_array_t properties;
    //协议列表
    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指针优化,之前的版本又叫index的其实一个意思苹果后来给这类优化方案起了名字NONPOINTER
        uintptr_t has_assoc         : 1; // 对象是否含有关联引用
        uintptr_t has_cxx_dtor      : 1; // 对象是否含有 C++ 或者 Objc 的析构器
        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; //对象是否正在执行析构函数(是否在释放内存)
        uintptr_t has_sidetable_rc  : 1; // 判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
        uintptr_t extra_rc          : 19; //  存储该对象的引用计数值减一后的结果
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    }; 
}; 

参考文章 objc_class ,isa_t

6,runloop相关内容

7,自动释放池相关内容

8,dealloc释放过程

对象内存销毁时刻表
1,调用-release 对象的引用计数为0
  a,对象正在被销毁,生命周期结束
  b,不在有新的 __weak 弱引用,否则将指向nil
  调用[self dealloc]
2,子类调用 -dealloc
  a,继承链最底层的子类调用 -dealloc
  b,MRC下则会手动释放实例变量
  c,继承关系中没一层父类调用 -dealloc
3,NSObject 调用 -dealloc
  调用object_dispose()方法
4,调用object_dispose()
  为C++对象们(iVars)调用 destructors
  为ARC对象们(iVars)调用 -release
  解除所有runtime 方法关联的对象
  解除所有 __weak 引用
  调用 free()

48F6BDE411B4BE47B2244486A72648A0.jpg

参考文章Dealloc还需要注意什么

9,如何动态替换实例对象的某个方法

  1. 可参考KVO的实现(动态创建一个子类,利用isa指针交换,在子类重写该方法,进行消息转发)
  2. 使用class_replaceMethod/class_addMethod函数在运行时对函数进行动态替换或增加新函数
  3. 直接用_objc_msgForward 走消息转发流程,然后在快转过程中进行替换

参考文章如何为一个实例动态替换方法

10,重排链表

题目描述

解题思路
1、通过双指针,找到链表中间节点,然后将之前链表分为前后两个链表(通过慢指针断开))
2、反转后链表
3、将前后两个链表进行合并,先前链表内容,然后再是后链表内容
4、如果还有两边不为空,则直接添加单最终链表的尾部

代码实现
 /**
 * 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)
    }
    
    //反转链表
    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,有序数组中删除重复项

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
    }

你可能感兴趣的:(iOS 美团面试整理)