阅读 56

iOS 面试题-2019.下

  1. 简要说一下autoreleasePool的数据结构

简单说是双向链表,每张链表头尾相接,有parent、child指针,每创建一个池子,会在首部创建一个哨兵对象作为标记,最外层池子的顶端会有一个next指针。当链表容量满了,就会在链表的顶端,并指向下一张表

  1. 说一下autoreleasePool的实现原理

autoreleasePool是一个延时release的机制,在自动释放池被销毁或耗尽时,会向池中的所有对象发送release消息,释放所有autorelease对象

autoreleasePool并没有单独的结构,而是由若干个autoreleasePoolPage作为结点以双向链表的形式组合而成

  1. 每一个指针代表一个加入到释放池的对象或者是哨兵对象,哨兵对象是在@autoreleasepool{}构建的时候插入的
  2. 当自动释放池pop的时候,所有哨兵对象之后的对象都会release
  3. 链表会在一个Page空间占满时进行增加,一个autoreleasePoolPage的空间被占满时,会新建一个autoreleasePoolPage对象连接链表,后来的autorelease对象在新的page加入
  1. 解释一下三次握手和四次挥手
  • 三次握手
  1. 由客户端向服务端发送SYN同步报文
  2. 当服务端收到SYN同步报文之后,会返回给客户端SYN同步报文和ACK确认报文
  3. 客户端会向服务端发送ACK确认报文,此时客户端和服务端的连接正式建立
  • 四次挥手
  1. 先由客户端向服务端发送FIN结束报文
  2. 服务端会返回给客户端ACK确认报文。此时,由客户端发起的断开连接已经完成
  3. 服务端会发送给客户端FIN结束报文和ACK确认报文
  4. 客户端会返回ACK确认报文到服务端,至此,由服务端方向的断开连接已经完成

拓展:SYN攻击

在三次握手过程中,Server发送SYN-ACK之后,收到ClientACK之前的TCP连接称为半连接half-open connect,此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击 一般有两种方式 1、客户端恶意不发送ACK 2、在发送给服务器的SYN报文段中提供虚假的IP地址,造成服务器永远收不到ACK

  1. 一个NSObject对象占用多少内存

一个指针变量所占用的大小,64bit8个字节,32bit4个字节

  1. 对象的isa指针指向哪里

instance对象的isa指针指向class对象,class对象的isa指针指向meta-class对象,meta-class对象的isa指针指向基类的meta-class对象,基类自己的isa指针也指向自己

  1. OC的类信息存放在哪里

成员变量的具体值存放在instance对象,对象方法、协议、属性、成员变量信息存放在class对象,类方法信息存放在meta-class对象

  1. 为什么使用runtime技术中的关联对象可以为类别添加属性

关联对象都由AssociationsManager管理,AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而mapkey是这个对象的指针地址,而这个mapvalue又是另外一个AssociationsHashMap,里面保存了关联对象的kv

  1. 用过哪些锁?哪些锁的性能比较高?谈下Objective-C都有哪些锁机制,你一般用哪个

常用的锁有NSLock、@synchronized代码块、信号量 dispatch_semaphore_t

os_unfair_lock(推荐) dispatch_semaphore(推荐) pthread_mutex(推荐) dispatch_queue(DISPATCH_QUEUE_SERIAL)(推荐) NSLock() NSCondition() @synchronized(最不推荐)

信号量性能最高

@synchronized代码块最方便

  1. 为什么一定要在主线程里面更新UI

UIKit不是线程安全的,容易产生UI更新上的混乱

  1. 类和结构体的区别

类是引用类型,结构体是值类型。结构体变量分配在栈,OC对象分配在堆。结构体只能封装属性,类不仅可以封装属性也可以封装方法

  1. 引用和指针的区别

指针的本质也就是变量,它不仅有自己的地址,也有它所存放的值,只不过这个值是地址而已

引用也就是指针常量,它是一个对象的别名,既然初始化了所指向的地址,那么它一定不为空,而且地址不可变 引用必须被初始化,指针不必 引用初始化以后不能被改变,指针可以改变所指的对象 不存在指向空值的引用,但存在指向空值的指针

  1. id类型、nilNilNULLNSNULL的区别

id类型是一个独特的数据类型,可以转换为任何数据类型,id类型的变量可以存放任何数据类型的对象,id声明的对象具有运行时特性,可以指向任意类型的对象

nil是一个实例对象值,如果我们要把一个对象设置为空的时候就用nil Nil是一个类对象的值,如果我们要把一个class的对象设置为空的时候就用Nil NULL指向基本数据类型的空指针C语言的变量的指针为空 NSNull是一个对象,它用在不能使用nil的场合

  1. weak的底层实现的原理是什么

weak表其实是一个hash哈希表,Key是所指对象的地址,Valueweak指针的地址数组

runtime维护了一个weak表,用于存储指向某个对象的所有weak指针,weak表其实是一个hash表,Key是所指对象的地址,valueweak指针的地址数组 为什么value是数组?因为一个对象可能被多个弱引用指针指向

  1. weak原理实现步骤

weak的实现原理可概括三步

  1. 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址
  2. 添加引用时:objc_initWeak函数会调用objc_storeWeak()函数,objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表
  3. 释放时,调用clearDeallocating函数,clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entryweak表中删除,最后清理对象的记录
  1. 编译过程做了哪些事情

Objective、Swift都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高

Objective、Swift二者的编译都是依赖于Clang + LLVM 不管是OC还是Swift,都是采用Clang作为编译器前端,LLVM(Low level vritual machine)作为编译器后端

  • 编译器前端:编译器前端的任务是进行语法、语义分析,生成中间代码。在这个过程中会进行类型检查,如果发现错误或者警告会标注出来在哪一行
  • 编译器后端:编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。LLVM优化器会进行BitCode的生成,链接器优化等等,LLVM机器码生成器会针对不同的架构,比如arm64等生成不同的机器码
  1. Linux命令之-Strings

strings命令是在对象文件或者二进制文件中查找可打印的字符串,常用来在二进制文件中查找字符串,与grep配合使用,例如一个用法就是在编译的so中定义字符串常量作为动态库的版本号,然后就可以使用strings+grep组合命令查看当前编译的so的版本号了。输入strings -h查看strings命令的用法,下面为具体用法实例

strings /lib/tls/libc.so.6 | grep GLIBC strings /Users/y**ar/Desktop/Demo2/Libraries/libiPhone-lib.a | grep "UIWebView" cat /Users/y**ar/Desktop/Demo2/Libraries/libiPhone-lib.a

  1. OC格式化打印

%d 整数 %02d 表示不足2位补0 %u 无符号整型 %f   浮点(双字节) %.2f 精度浮点数,只保留两位小数 %x,%X   十六进制整数 %o   八进制整数 %p   指针 %s   C字符串 %c   字符

附:我的博客地址