将来跳槽用

2020-07-05

2020-07-10  本文已影响0人  恍然如梦_b700
  1. nil:对象为空
  2. Nil:类为空
  3. NULL:基本数据对象指针为空
  4. NSNull:集合对象无法包含 nil 作为其具体值,如NSArray、NSSet和NSDictionary。相应地,nil值用一个特定的对象 NSNull 来表示。NSNull 提供了一个单一实例用于表示集合对象属性中的的nil值。

19.iOS中内存管理?
iOS中内存管理的方式主要有三大,1.taggedPointer,2.NONPOINTER_ISA,3.散列表。

20. KVO
KVO的实现用了一种叫 isa-swizzling 的技术。
当一个对象的一个属性注册了观察者后,被观察对象的isa指针的就指向了一个系统为我们生成的中间类NSKVONotifying_,而不是我们自己创建的类。在这个类中,系统为我们重写了被观察属性的setter方法。
通过 object_getClass(id obj) 方法可以获得实例对象真实的类(isa指针的指向)。
可以看到 _NSSetObjectValueAndNotify 还是调用了 willChangeValueForKey: 和 didChangeValueForKey: 来进行手动通知的。

21.UIView & CALayer的区别
联系
每一个 view 中都有一个 layer,view 持有并管理这个 layer,且这个 view 是 layer 的代理。
区别
UIView 负责响应事件,参与响应链,为 layer 提供内容。
CALayer 负责绘制内容,动画。

22. 什么是离屏渲染
GPU 在当前屏幕缓冲区之外另开一片内存空间进行渲染操作。
GPU 在绘制时没有回头路,某一个 layer 在被绘制之后就已经与之前的若干层形成一个整体了,无法再次修改。
对于一些有特殊效果的 layer,GPU 无法通过单次遍历就能完成渲染,只能另外申请一片内存区域,借助这个临时区域来完成更加复杂、多次的修改与裁减操作。
大小是屏幕像素点的2.5倍
需要创建新的渲染缓冲区,会存在不小的内存开销
且需要切换渲染上下文 Context Switch,会浪费很多时间

23 http 和https

24.什么是中间人攻击
中间人攻击的定义:中间人攻击是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。
简单来说,攻击者在请求和响应传输途中,拦截并篡改内容

1.@autoreleasepool展开来其实就是objc_autoreleasePoolPush和objc_autoreleasePoolPop,但是这两个函数也是封装的一个底层对象AutoreleasePoolPage,实际对应的是AutoreleasePoolPage::push和AutoreleasePoolPage::pop

2.autoreleasepool本身并没有内部结构,而是一种通过AutoreleasePoolPage为节点的双向链表结构

3.根据AutoreleasePoolPage双向链表的结构,可以看到当调用objc_autoreleasePoolPush的时候实际上除了初始化poolpage对象属性之外,还会插入一个POOL_SENTINEL哨兵,用来区分不同autoreleasepool之间包裹的对象。

4.当对象调用 autorelease 方法时,会将实际对象插入 AutoreleasePoolPage 的栈中,通过next指针移动。

5.autoreleasePoolPage的结构字段上面有介绍,其中每个双向链表的node节点也就是poolpage对象内存大小为4096,除了基础属性之外,外插一个POOL_SENTINEL,每出现一个@autorelease就会有一个哨兵,剩下的通过begin和end来标识是否存储满,满了就会重新创建一个poolpage来链接链表,按照这个套路,出现一个PoolPush就创建一个哨兵,出现一个对象的autorelease,就增加一个实际的对象,满了就创建新的链表节点这样衍生下去

6.AutoreleasePoolPage::pop那么当调用pop的时候,会传入需要drain的哨兵节点,遍历该内存地址上方所有对象,直到遇到对应的哨兵,然后释放栈中遍历到的对象,每删除一页就修正双向链表的指针,最后两张图很容易理解

7.ARC下,直接调用上面的方法,整个线程都被自动释放池双向链表管理,Push创建的时候插入哨兵对象,当我们在内部写代码的时候,会自动添加Autorelease,对象会加入到在哨兵节点之间,加入到next指针上,一个个往后移,满了4096就换下一个poolPage对象节点来存储,出了释放池,会调用pop,传入自动释放池的哨兵给pop,然后遍历哨兵内存地址之后的所有对象执行release,最后吧next指针移到目标哨兵

App启动后,苹果在主线程 RunLoop 里注册了两个Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()

第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。

第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush()释放旧的池并创建新池;Exit(即将退出Loop)时调用 objc_autoreleasePoolPop()来释放自动释放池。这个 Observerorder2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。

25 categray相关
https://www.jianshu.com/p/78e18cdbe23f

+ (instancetype)sharedInstance
{
    /*定义相应类实例的静态变量;
    意义:函数内定义静态变量,无论该函数被调用多少次,
         在内存中只初始化一次,并且能保存最后一次赋的值
    */
    static ClassName *instance = nil;
    /*定义一个dispatch_once_t(其实也就是整型)静态变量,
    意义:作为标识下面dispatch_once的block是否已执行过。
         static修饰会默认将其初始化为0,当值为0时才会执行block。
         当block执行完成,底层会将onceToken设置为1,这也就是为什
         么要传onceToken的地址(static修饰的变量可以通过地址修改
         onceToken的值),同时底层会加锁来保证这个方法是线程安全的
    */
    static dispatch_once_t onceToken;
    /*只要当onceToken == 0时才会执行block,否则直接返回静态变量instance*/
    dispatch_once(&onceToken, ^{
        instance = [[ClassName alloc] init];
        //...
    });
    return instance;
}

散列冲突
开放寻址法(open addressing)和链表法(chaining)
我们用装载因子(load factor)来表示空位的多少。

装载因子的计算公式是:

散列表的装载因子 = 填入表中的元素个数 / 散列表的长度

装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

散列表中,每个“桶(bucket)”或者“槽(slot)”会对应一条链表,所有散列值相同的元素我们都放到相同槽位对应的链表中
当数据量比较小、装载因子小的时候,适合采用开放寻址法
基于链表的散列冲突处理方法比较适合存储大对象、大数据量的散列表,而且,比起开放寻址法,它更加灵活,支持更多的优化策略,比如用红黑树代替链表。
哈希算法:
将任意长度的二进制值串映射为固定长度的二进制值串,这个映射的规则就是哈希算法,而通过原始数据映射之后得到的二进制值串就是哈希值
哈希算法应用:
分别是安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。

#define MAXSIZE 10000
typedef struct
{
    //r[0]用作哨兵或临时变量
    int r[MAXSIZE+1];
    int length;
}SqList;

void swap(SqList *L, int i, int j) {
    int a = L->r[i];
    int b = L->r[j];
    a ^= b;
    b ^= a;
    a ^= b;
}

int Partition(SqList *L, int low, int high) {
    int pivotkey;
    pivotkey = L->r[low];
    while (low < high) {
        while (low < high && L->r[high] >= pivotkey)
            high--;
        swa(L, low, high);
        while (low < high && L->r[low] <= pivotkey)
            low++;
        swa(L, low, high);
    }
    return low;
}

void QSort(SqList *L, int low, int high) {
    int pivot;
    if (low<high) {
        pivot = Partition(L, low, high);
        QSort(L, low, pivot-1);
        QSortd(L, pivot+1, high);
    }
}

void QuickSort3(SqList *L) {
    QSortd(L, 0, L->length);
}

值类型存du取速度快,引用类型zhi存取速度慢。

2、用途上的区别

值类型表示实际数据,引用类dao型表示指向存储在内存堆中的数据的指针或引用。
3、来源上的区别
值类型继承自System.ValueType,引用类型继承自System.Object

4、位置上的区别

值类型的数据存储在内存的栈中,引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。
5、类型上的区别

值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。

6、保存位置上的区别

值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中。

上一篇 下一篇

猜你喜欢

热点阅读