面试题整理

KVO原理及自定义KVO

  • KVO原理
    我们注册监听的时候,会对注册者动态的创建一个子类对象,然后底层找方法的的isa指针就变成指向新创建的子类对象。当改变注册对象某个属性的时候,就重写属性的set方法来进行监听。
  • 总结自定义一个KVO思路:
    1.自定义一个类,继承 [self class]的一个子类
    2.重写父类的属性setter方法
    3.调用observeValueForKeyPath:ofObject:change:context:方法,回调到ViewController中去。
    具体原理及自定义KVO详见:https://blog.csdn.net/u014247354/article/details/78403567

消息转发机制原理

消息转发是在运行时进行的,大致分为两个阶段:第一阶段是先检查接收者,看是否能通过runtime动态添加一个方法,来处理这个unknown selector的消息;第二阶段就是完整的消息转发机制,首先会先查看有没有其它对象能够处理该消息,如果没有,就把该消息的全部信息封装到NSInvocation对象中,看那个对象能否处理,如果还无法处理,就查看继承树中的类是否能够处理该消息,如果到NSObject之前都无法处理该消息,那么最后就会调用NSObject类的doesNotRecognizeSelector方法来抛出异常,表明调用的方法不存在。
具体转发机制详见:https://www.cnblogs.com/fishbay/p/7216197.html?utm_source=itdadao&utm_medium=referral

说说你理解weak属性

  • weak实现原理
    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.实现weak后,为什么对象释放后会自动为nil?
    runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为 0 的时候会 dealloc,假如 weak 指向的对象内存地址是 a ,那么就会以 a 为键, 在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil 。
    2.当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?
    1、调用objc_release
    2、因为对象的引用计数为0,所以执行dealloc
    3、在dealloc中,调用了_objc_rootDealloc函数
    4、在_objc_rootDealloc中,调用了object_dispose函数
    5、调用objc_destructInstance
    6、最后调用objc_clear_deallocating,详细过程如下:
    a. 从weak表中获取废弃对象的地址为键值的记录
    b. 将包含在记录中的所有附有 weak修饰符变量的地址,赋值为 nil
    c. 将weak表中该记录删除
    d. 从引用计数表中删除废弃对象的地址为键值的记录

假如Controller太臃肿,如何优化?

1.将网络请求抽象到单独的类中
2.将界面的封装抽象到专门的类中
3.构造 ViewModel
4.专门构造存储类

项目中网络层如何做安全处理?

1、尽量使用https
https可以过滤掉大部分的安全问题。https在证书申请,服务器配置,性能优化,客户端配置上都需要投入精力,所以缺乏安全意识的开发人员容易跳过https,或者拖到以后遇到问题再优化。https除了性能优化麻烦一些以外其他都比想象中的简单,如果没精力优化性能,至少在注册登录模块需要启用https,这部分业务对性能要求比较低。
2、不要传输明文密码
不知道现在还有多少app后台是明文存储密码的。无论客户端,server还是网络传输都要避免明文密码,要使用hash值。客户端不要做任何密码相关的存储,hash值也不行。存储token进行下一次的认证,而且token需要设置有效期,使用refresh token去申请新的token。
3、Post并不比Get安全
事实上,Post和Get一样不安全,都是明文。参数放在QueryString或者Body没任何安全上的差别。在Http的环境下,使用Post或者Get都需要做加密和签名处理。
4、不要使用301跳转
301跳转很容易被Http劫持攻击。移动端http使用301比桌面端更危险,用户看不到浏览器地址,无法察觉到被重定向到了其他地址。如果一定要使用,确保跳转发生在https的环境下,而且https做了证书绑定校验。
5、http请求都带上MAC
所有客户端发出的请求,无论是查询还是写操作,都带上MAC(Message Authentication Code)。MAC不但能保证请求没有被篡改(Integrity),还能保证请求确实来自你的合法客户端(Signing)。当然前提是你客户端的key没有被泄漏,如何保证客户端key的安全是另一个话题。MAC值的计算可以简单的处理为hash(request params+key)。带上MAC之后,服务器就可以过滤掉绝大部分的非法请求。MAC虽然带有签名的功能,和RSA证书的电子签名方式却不一样,原因是MAC签名和签名验证使用的是同一个key,而RSA是使用私钥签名,公钥验证,MAC的签名并不具备法律效应。
6、http请求使用临时密钥
高延迟的网络环境下,不经优化https的体验确实会明显不如http。在不具备https条件或对网络性能要求较高且缺乏https优化经验的场景下,http的流量也应该使用AES进行加密。AES的密钥可以由客户端来临时生成,不过这个临时的AES key需要使用服务器的公钥进行加密,确保只有自己的服务器才能解开这个请求的信息,当然服务器的response也需要使用同样的AES key进行加密。由于http的应用场景都是由客户端发起,服务器响应,所以这种由客户端单方生成密钥的方式可以一定程度上便捷的保证通信安全。
7、AES使用CBC模式
不要使用ECB模式,记得设置初始化向量,每个block加密之前要和上个block的秘文进行运算。

main()之前的过程有哪些?

1、dyld 开始将程序二进制文件初始化
2、交由ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)
3、由于runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理
4、runtime 接手后调用map_images做解析和处理
5、接下来load_images 中调用call_load_methods方法,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法
6、至此 所有的信息都被加载到内存中
7、最后dyld调用真正的main函数
注意:dyld会缓存上一次把信息加载内存的缓存,所以第二次比第一次启动快一点

数据持久化方式

1、NSUserDefaults
优点:简单易用,不需要我们去创建文件,数据会直接存储在沙盒内prefrences文件夹下的plist文件中
缺点:不能存储自定义的过于复杂的对象
所以,我们一般用来存储个人的偏好设置,存储本地登陆的账号密码,判断用户是否第一次启动程序等这些简单的少量数据.
2、简单对象文件读写
优点:易用性强,对于系统提供的基础类型NSString,NSArray,NSDictionary,NSData可以根据文件路径可以借助系统提供的方法直接进行数据的本地存储.
缺点:只能存储系统提供的四种基本类型,不能进行我们自定义的对象进行持久化; 新的数据会将之前的数据进行覆盖,限定了我们也是只能进行少量数据的存储.
所以,我们一般用于存储系统提供的基本类型的小量存储,不适合大量数据的存储.
3、复杂对象文件读写(归档与反归档)
优点:易用性强,最突出的是可以将复杂的对象写入文件,可以归档集合类
缺点:数据的存储其实原理和简单对象的写入原理相同,也会进行旧数据的覆盖. 而且随着对象量的增加工作量会随之增加.
所以,我们一般用来对存储少量的复杂对象.使用时需要注意旧数据覆盖的问题.
4、sqlite3数据库
优点:当前市面上使用较多的一种数据持久化方式; 可以存储大量的数据,数据不会进行覆盖;而且进行大量数据存储 检索非常高效.
缺点:基于C语言接口,使用起来较为麻烦;易用性不强;还需要熟记基本的SQL语句;
所以,我们一般会用来存储大量的数据.
5、CoreData技术
优点:支持sqlite,xml,plist等多种数据的存储,对于sqlite的存储相较于sqlite3来说使用方便,提供的可视化建模和高度的抽象封装使得代码量也大大减少;另外一个显著的优点是可以很方便的进行数据库的数据迁移.另外使用者不需要在进行数据库的学习掌握就可以完美使用
缺点:使用步骤和牵扯的类比较多,再加上抽象度较高使得CoreData使用较为麻烦
所以,对于大量的数据的本地存储我们一般使用CoreData.另外这项技术是由苹果在iOS 3.0引入,对于初学者感觉吃力,但是掌握住之后定会爱不释手.

ios多线程中对同一资源访问形成资源竞争的处理方式

1、@synchronized(id anObject)
会自动对参数对象加锁,保证临界区内的代码线程安全(最简单的方法)

@synchronized(self)  
{  
       // 这段代码对其他 @synchronized(self) 都是互斥的         
       // self 指向同一个对象  
}   

2、NSLock
NSLock对象实现了NSLocking protocol,包含几个方法:
lock,加锁
unlock,解锁
tryLock,尝试加锁,如果失败了,并不会阻塞线程,只是立即返回NO
lockBeforeDate:,在指定的date之前暂时阻塞线程(如果没有获取锁的话),如果到期还没有获取锁,则线程被唤醒,函数立即返回NO

NSLock *theLock = [[NSLock alloc] init];   
if ([theLock lock])   
{  
   //do something here  
   [theLock unlock];   
}  

3、NSRecursiveLock,递归锁
NSRecursiveLock,多次调用不会阻塞已获取该锁的线程。

NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];   
 void MyRecursiveFunction(int value)   
{   
 [theLock lock];   
 if (value != 0)   
 {   
    –value;   
    MyRecursiveFunction(value);   
 }  
 [theLock unlock];   
}   
  
 MyRecursiveFunction(5);  

4、NSConditionLock,条件锁
NSConditionLock,条件锁,可以设置条件

//公共部分  
id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA];   
       
 //线程一,生产者  
 while(true) {   
        [condLock lockWhenCondition:NO_DATA];   
        //生产数据  
        [condLock unlockWithCondition:HAS_DATA];   
}  
       
 //线程二,消费者  
 while (true) {   
        [condLock lockWhenCondition:HAS_DATA];   
        //消费  
        [condLock unlockWithCondition:NO_DATA];   
}  

5、NSDistributedLock,分布锁
NSDistributedLock,分布锁,文件方式实现,可以跨进程
用tryLock方法获取锁。
用unlock方法释放锁。
如果一个获取锁的进程在释放锁之前挂了,那么锁就一直得不到释放了,此时可以通过breakLock强行获取锁。

为什么 block 要使用 copy

block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。

系统对象的 copy 与 mutableCopy 方法

不管是集合类对象(NSArray、NSDictionary、NSSet ... 之类的对象),还是非集合类对象(NSString, NSNumber ... 之类的对象),接收到copy和mutableCopy消息时,都遵循以下准则:
1、 copy 返回的是不可变对象(immutableObject);如果用copy返回值调用mutable对象的方法就会crash。
2、 mutableCopy 返回的是可变对象(mutableObject)。

1、非集合类对象的copy与mutableCopy
在非集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
对可变对象进行copy和mutableCopy都是内容复制
2、集合类对象的copy与mutableCopy
在集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
对可变对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对集合内的对象元素仍然是指针复制。(即单层内容复制)
【总结】
只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)!

你可能感兴趣的:(面试题整理)