OC相关(二)

2023-08-07  本文已影响0人  开发者老岳
25. KVO的实现原理,如果手动实现KVO?

(1) KVO 是基于 runtime 机制实现的
(2) 当一个对象 (假设是person对象,对应的类为 JLperson) 的属性值age发生改变时,系统会自动生成一个继承自JLperson的类NSKVONotifying_JLPerson,在这个类的 setAge 方法里面调用

 [super setAge:age]
 [self willChangeValueForKey:@"age"];
 [self didChangeValueForKey:@"age"];

三个方法,而willChangeValueForKey:didChangeValueForKey:两个方法内部会主动调用-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
方法,在该方法中可以拿到属性改变前后的值。
(3) 最后把这个JLperson对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类NSKVONotifying_JLPerson,对象就神奇的变成了新创建的子类NSKVONotifying_JLPerson的实例。

26.谈下iOS开发中知道的哪些锁?

相关文章:

  • @synchronized
  • NSLock 对象锁(个人理解是互斥锁)
  • NSRecursiveLock 递归锁
  • NSConditionLock 条件锁
  • pthread_mutex 互斥锁(C语言)
  • dispatch_semaphore 信号量实现加锁(GCD)
  • OSSpinLock (暂不建议使用,原因参见这里
  • 哪个性能最差?
  • SD和AFN使用的哪个?
    @synchronized用的最多,其次就是信号量和NSLock,其余的都很少用。
  • 一般开发中你最常用哪个? 哪个锁apple存在问题又是什么问题?
    NSLock。
    OSSpinLock。
  • OSSpinLock为什么不安全了
    如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。
  • 什么情况下会有死锁?
  • 在一个串行队列里做同步。如:
  • 在一个递归里用互斥锁。使用锁最容易犯的一个错误就是在递归或循环中造成死锁

    如下代码中,因为在线程1中的递归block中,锁会被多次的lock,所以自己也被阻塞了。 死锁代码
  • 此处将NSLock换成NSRecursiveLock,便可解决问题。NSRecursiveLock类定义的锁可以在同一线程多次lock,而不会造成死锁。递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操作。只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。 改成递归锁就不会死锁了
  • 互斥锁有哪些?互斥锁和自旋锁的区别是啥?
  • 互斥锁:如果一个线程无法获取互斥量,该线程会被直接挂起,不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活被挂起的线程。互斥锁会使得线程阻塞,阻塞的过程又分两个阶段,第一阶段是会先空转,可以理解成跑一个 while 循环,不断地去申请加锁,在空转一定时间之后,线程会进入 waiting 状态,此时线程就不占用CPU资源了,等锁可用的时候,这个线程会立即被唤醒。
  • 自旋锁: 如果一个线程需要获取自旋锁,该锁已经被其他线程占用,该线程不会被挂起,而是不断消耗CPU时间,一直试图获取自旋锁。
  • 总结:最大的区别,互斥锁是挂起等待,等待时不占用CPU,自旋锁会不停地试图获取锁,会一直占用CPU。
26. 串行队列和同步锁两者在保护线程安全上的性能对比。
27. iOS下如何实现指定线程数目的线程池?
28. 数据库建表的时候索引有什么用?

在数据量大的时候,快速提高查找速度。

//在表上创建一个简单的索引。允许使用重复的值。
CREATE INDEX index_name ON table_name (column_name);
////在表上创建一个唯一的索引。唯一的索引意味着两个行不能拥有相同的索引值。
CREATE UNIQUE INDEX index_name ON table_name (column_name);

注意事项:

29. 介绍下iOS设备获取唯一设备号的历史变迁。

iOS5之后,udid被废弃。
iOS6之后,可以用idfa,不过idfa用户可以在设置—通用—隐私里还原。
iOS7之后,udid被彻底限制。有段时间可以用open udid替代,将其存在剪切板中。但是有缺点,如果用户完全删除了open udid sdk 的app,然后重启设备,就会重新生成新的open udid。
后来open udid停止更新了。
idfa或UUID保存在keychain里。(UUID每次生成都会变)。

30. 如何使用runtime hook一个class的某个方法,又如何hook某个instance的方法?

消息转发里有专门的方法。

31. UIView、CoreAnimation和CoreGraphics的关系。
layer.backgroundColor = [UIColor redColor].CGColor;  

首先,图层layer的类型是CALayer,它是CoreAnimation中的类。前面说CoreAnimation是跨平台的,为了跨平台的特性,它的backgroundColor属性就不能使用UIColor类型了,因为UIKit只能使用于iOS,而CoreGraphics框架是跨平台的,所以CALayer类的backgroundColor属性就使用了CGColor类型。所以使用时在赋值前要先进行转换,将UIKit中的东西转换为CoreGraphics中的类型。

32.通过[UIImage imageNamed:]生成的对象什么时候被释放?

用UIImage加载本地图像最常用的是下面三种:

  1. imageNamed方法
[UIImage imageNamed:ImageName];
  1. imageWithContentsOfFile方法
NSString *thumbnailFile = [NSString stringWithFormat:@"%@/%@.png", [[NSBundle mainBundle] resourcePath], fileName];
UIImage *thumbnail = [UIImage imageWithContentsOfFile:thumbnailFile];
  1. initWithContentsFile方法
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath]

这两种方法都有各自的优缺点,那么有什么办法能够把其优点结合起来呢,答案是hook;
首先hook imageNamed方法,然后在我们自己的方法中进行如下判断:
定义一个本地缓存图片的最大size
判断本地Bundle中是否存在该图片,不存在直接返回到原imageNamed方法,存在则继续
从缓存中直接读取,如果存在该图片,直接返回,否则继续
判断该图片是否大于size,如果大于则调用原imageWithContentsOfFile方法返回UIImage
如果该图片不大于size则返回,并存入缓存
接下来hookimageWithContentsOfFile方法,步骤也是类似,就不多做阐述,直接看代码;
https://elliotsomething.github.io/2016/03/01/iOS-%E4%B9%8B-imageNamed%E5%92%8CimageWithContentsOfFile%E4%BC%98%E5%8C%96/

33. 如何终止正在运行的工作线程?
  • NSOperationQueue:
[self.blockOperation cancel];
[self.invocationOperation cancel];
[self.queue cancelAllOperations];

注:设置优先级只针对同一队列中的操作,而且在必须start或者加入队列之前设置才有效果。操作的执行优先级还取决于其依赖关系和添加队列的顺序,如果其依赖的操作未执行完毕或者和其相同优先级的操作在其之前添加到队列,那该操作的执行顺序要延后。

  • 暂停和继续: 可以通过以下方法暂停和继续操作队列。
[self.queue setSuspended:YES]; //暂停
[self.queue setSuspended:NO];  //继续

注:暂停一个操作队列不会导致正在执行的操作任务中途暂停,只是简单地阻止调度新操作任务执行。

  • GCD:
  • 法1:通过dispatch_block_cancel
    不过这个方法只能iOS8以后才能用,而且也是只能停止还未开始执行的任务。
  • 法2:用于标记block是否需要取消,通过if判断是否需要return。
34. 分析下SDWebImage (q3:内部做Decoder的原因 (典型的空间换时间)).
35.你认为开发中那些导致crash?crash的收集和定位bug的方式谈下
36. Autorelease的原理?ARC的工作原理。
37. Runloop的原理,它是怎么休眠的?

就是一个do-while循环,很多地方底层都是依赖Runloop,如定时器,自动释放池等。
没有任务的超过一定时间,就会自动进入休眠。

38. 如果页面 A 跳转到 页面 B,A 的 viewDidDisappear 方法和 B 的 viewDidAppear 方法哪个先调用?

viewDidAppear

39. 怎么判断一个cell是否显示在屏幕上?
40.倒计时如何实现 ?
41. 熟悉 CocoaPods 么?能大概讲一下工作原理么?
42. 子线程里创建对象时是否需要创建自动释放池。

需要。
默认主线的运行循环(runloop)是开启的,子线程的运行循环(runloop)默认是不开启的,也就意味着子线程中不会创建autoreleasepool,所以需要我们自己在子线程中创建一个自动释放池。(子线程里面使用的类方法都是autorelease,就会没有池子可释放,也就意味着后面没有办法进行释放,造成内存泄漏。)----在主线程中如果产生事件那么runloop才回去创建autoreleasepool,通过这个道理我们就知道为什么子线程中不会创建自动释放池了,因为子线程的runloop默认是关闭的,所以他不会自动创建autoreleasepool,需要我们手动添加。

43.谈谈常用的设计模式。

单例模式
工厂模式
观察者模式
代理模式

44.KVC的实现原理。KVC是如何通过key找到value的?
setValue:forKey:的搜索方式
  1. 首先搜索setKey:方法。(key指成员变量名,首字母大写)。
  2. 若未找到setter方法,若类的accessInstanceVariablesDirectly方法返回YES(默认返回YES),则按_keykey_isKey的顺序搜索成员名。
  3. 如果没有找到成员变量,调用setValue:forUnderfinedKey:方法(然后应该是抛出异常)。
valueForKey:的搜索方式:
  1. 首先按getKeykeyisKey_key的顺序查找getter方法,找到直接调用。如果是BOOLint等内建值类型,会做NSNumber的转换。
  2. 上面的getter没找到,查找countOfKeyobjectInKeyAtIndex:keyAtIndexes格式的方法。如果 countOfKey和另外两个方法中的其中一个找到,那么就会返回一个NSArray对象。
  3. 还没找到,查找countOfKey:enumeratorOfKey:memberOfKey:格式的方法。如果这三个方法都找到,那么就返回一个NSSet对象。
  4. 再没找到,调用valueForUndefinedKey:方法。
  • 注:有的博客说iskey_isKey也会被调用,但经过我的代码尝试这俩并不行。
setValue:forKeyPath:

该方法能利用运算符一层一层往内部访问属性,如:

NSString *avg= [persons valueForKeyPath:@"Person.@avg.age"];

实现原理就是用Runtime动态的去做上面的事,比如setValue:forKey:就是先动态的去找有没有setter方法,没有的话就动态的找有没有相关的成员变量,再没有的话就抛出异常。

  1. 必须用在集合对象上或普通对象的集合属性上
  2. 简单集合运算符有@avg, @count , @max , @min ,@sum,
  3. 格式 @"@sum.age"@"集合属性[.@max.age](mailto:.@max.age)"
45. Block的本质是什么?

简单地说就是把函数当成一个参数进行传值,本质跟NSObject一样,都是一些结构体定义的,其中也都有isa指针。

46. 如何在异步下载时候, 取消下载, 保证流量不浪费

NSOperation,有cancel方法。(答案可能不对)

47.id、NSObject *、id<NSObject>、instancetype的区别。
48. Block和函数指针的区别。
49. 如何优化开机速度?
  1. 删除没用到的framework、类、静态变量、图片等。
  2. 删减没有被调用到或者已经废弃的方法。
  3. 尽量不要在load方法里增加方法,可以放到initialize里。
  1. 不使用xib、sb,直接视用代码加载首页视图。
  2. NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,如果文件太大的话一次能读取到内存中可能很耗时,不要在这里保存图片。
  3. 每次用NSLog方式打印会隐式的创建一个Calendar,因此需要删减启动时各业务方打的log,或者仅仅针对内测版输出log。
  4. 首次加载的控制器的viewDidLoad以及viewWillAppear方法里尽量少做或延时加载。
  5. 压缩资源图片。
50. App内存你是如何分析的?
51. 怎么完成后期检测, 优化。

个人理解:

52. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的。

本质就是:@property = ivar + getter + setter;
自动合成,通过@synthesize自动合成了成员变量和getter和setter方法。再往本质里说,类本身都是一些结构体,里面有成员变量列表、方法列表等一系列参数,自动合成的过程就是往成员列表里、和方法列表里增加相应的内容。

上一篇 下一篇

猜你喜欢

热点阅读