面试总结

1,KVC的实现原理

当调用[persion setValue:@”lv” forKey:@”name”]的代码时,底层的执行机制如下:

  1. 程序优先调用setName方法,代码通过setter方法完成设置
  2. 如果没有找到setName方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUNdefinedKey:方法,不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为name的成员变量,无论该变量是在类接口部分定义,还是在类实现部分定义,也无论用了什么样的访问修饰符,只在存在以命名的变量,KVC都可以对该成员变量赋值。
  3. 如果该类即没有set:方法,也没有_成员变量,KVC机制会搜索_is的成员变量
  4. 和上面一样,如果该类即没有set:方法,也没有_和_is成员变量,KVC机制再会继续搜索和is的成员变量。再给它们赋值
  5. 如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUNdefinedKey:方法,默认是抛出异常(大部分情况下,我们没有重载这个方法,程序是会直接carsh的)
    果想让这个类内部禁用KVC,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set:属性名时,会直接用setValue:forUNdefinedKey:方法。如果返回了YES,且没有实现get方法,再以kvc方式取值会crash

KVC/KVO 进阶(一) 底层原理

2,KVO的实现原理

  1. KVO是基于runtime机制实现的
  2. 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
  3. 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person
  4. 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
  5. 键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
  6. 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类

IOS-详解KVO底层实现

3,NSRunloop

每个线程(NSThread)对象中内部都有一个run loop(NSRunLoop)对象用来循环处理输入事件,处理的事件包括两类,一是来自Input sources的异步事件,一是来自Timer sources的同步事件;
run Loop在处理输入事件时会产生通知,可以通过Core Foundation向线程中添加run-loop observers来监听特定事件,以在监听的事件发生时做附加的处理工作。
[图片上传失败...(image-4cda68-1532508928146)]
每个run loop可运行在不同的模式下,一个run loop mode是一个集合,其中包含其监听的若干输入事件源,定时器,以及在事件发生时需要通知的run loop observers。运行在一种mode下的run loop只会处理其run loop mode中包含的输入源事件,定时器事件,以及通知run loop mode中包含的observers。

  • Default模式
    定义:NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)
    描述:默认模式中几乎包含了所有输入源(NSConnection除外),一般情况下应使用此模式。
  • Connection模式
    定义:NSConnectionReplyMode(Cocoa)
    描述:处理NSConnection对象相关事件,系统内部使用,用户基本不会使用。
  • Modal模式
    定义:NSModalPanelRunLoopMode(Cocoa)
    描述:处理modal panels事件。
  • Event tracking模式
    定义:UITrackingRunLoopMode(iOS) NSEventTrackingRunLoopMode(cocoa)
    描述:在拖动loop或其他user interface tracking loops时处于此种模式下,在此模式下会限制输入事件的处理。例如,当手指按住UITableView拖动时就会处于此模式。
  • Common模式
    定义:NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)
    描述:这是一个伪模式,其为一组run loop mode的集合,将输入源加入此模式意味着在Common Modes中包含的所有模式下都可以处理。在Cocoa应用程序中,默认情况下Common Modes包含default modes,modal modes,event Tracking modes.可使用CFRunLoopAddCommonMode方法想Common Modes中添加自定义modes。

iOS中NSRunLoop的模式
NSRunLoop原理详解——不再有盲点

4,多线程

NSThread,GCD,NSOperation

NSThread

阻塞线程的方法

[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
GCD

同步任务,异步任务,同步队列,异步队列,分组

面试点:
GCD 控制线程数量
GCD 任务分组
主线程刷新UI
线程锁死
线程安全

信号量:
是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。

其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//创建
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//等待降低信号量
dispatch_semaphore_signal(semaphore);//增加信号量

######dispatch_barrier_async

注意事项

NSOperation

NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

为什么要使用 NSOperation、NSOperationQueue?

可添加完成的代码块,在操作完成后执行。
添加操作之间的依赖关系,方便的控制执行顺序。
设定操作执行的优先级。
可以很方便的取消一个操作的执行。
使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled。

iOS多线程:『NSOperation、NSOperationQueue』详尽总结
iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用

5,锁

iOS 各种锁机制

  1. @synchronized关键字加锁
  2. dispatch_semaphore信号量锁
  3. NSLock
  4. NSRecursiveLock递归锁
  5. NSConditionLock条件锁
  6. NSCondition

总的来说:
OSSpinLock和dispatch_semaphore的效率远远高于其他。
@synchronized和NSConditionLock效率较差。
鉴于OSSpinLock的不安全,所以我们在开发中如果考虑性能的话,建议使用dispatch_semaphore。
如果不考虑性能,只是图个方便的话,那就使用@synchronized。

互斥锁属性PTHREAD_MUTEX_RECURSIVE
探讨iOS开发中各种锁

6. Block为什么用copy修饰

Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的

<栈 :由系统维护的局部变量 是存在栈上的,其生命周期随函数的生命周期>

<堆 :由程序员申请空间地址,由程序员手动释放,生命周期受到程序员控制>

使用retain也可以,因为block的retain行为默认是用copy的行为实现的,block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。

block作为类的属性时用copy

7. 静态库和动态库

库是共享程序代码的方式,一般分为静态库和动态库;库实现了iOS程序的模块化,将某些特定的功能模块化为库的格式方便分享和使用!

2.静态库和动态库有什么特点?

异同点:

静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。

动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序可以共用,节省内存。

共同点:

静态库和动态库都是闭源库,只能拿来满足某个功能的使用,不会暴露内部具体的代码信息,而从github上下载的第三方库大多是开源库

3.这两种库都有哪些文件格式?

静态库:.a和.framework

动态库:.dylib和.framework(系统直接提供给我们的framework都是动态库!)

作者:公子墨香
链接:https://www.jianshu.com/p/c8366e4f9378
來源:
著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

8. RunTime

常用应用:

  1. 发送消息
  2. 交换方法
  3. 动态添加方法
  4. 给分类添加属性(类关联)

runtime机制和使用
从runtime开始: 理解面向对象的类到面向过程的结构体
从runtime开始: 深入理解OC消息转发机制
从runtime开始: 理解OC的属性property
从runtime开始: 实践Category添加属性与黑魔法method swizzling
从runtime开始: 深入weak实现机理

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

APP启动时间由2部分组成

  1. main之前的系统dylib(动态链接库)和自身App可执行文件的加载的时间
  2. main之后执行didFinishLaunchingWithOptions:结束前的时间
  1. 系统先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境。
  2. 开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。
  3. 当所有依赖库的初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类机构初始化,然后调用所有的load方法。最后dyld返回main函数地址,main函数被调用,我们便来到程序入口main函数。

10. OC的反射机制

反射机制的概念:
对于任意一个类,都能够知道这个类的都有属性和方法
对于任意一个对象,都能够调用它的任意一个方法和属性
这种动态获取的信息以及动态调用对象的方法的 功能成为Java语言的反射机制。

  oc反射机制有三个用途:
  1.获得Class
  2.检查继承关系
  3.动态的调用方法
  1. 直接通过方法名来调用。[person show];
  2. 利用performSelector.
  3. NSInvocation来调用.

11. block的实质是什么?一共有几种block?都是什么情况下生成的?

block:本质就是一个object-c对象.
block:存储位置,可能分为3个地方:代码区,堆区、栈区(ARC情况下会自动拷贝到堆区,因此ARC下只能有两个地方:代码区、堆区)
代码区:不访问栈区的变量(如局部变量),且不访问堆区的变量(alloc创建的对象),此时block存放在代码区。
堆区:访问了处于栈区的变量,或者堆区的变量,此时block存放在堆区。–需要注意实际是放在栈区,在ARC情况下会自动拷贝到堆区,如果不是ARC则存放在栈区,所在函数执行完毕就回释放,想再外面调用需要用copy指向它,这样就拷贝到了堆区,strong属性不会拷贝、会造成野指针错区。

2.为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?
默认情况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量的内存的指针。
使用__block修饰后的变量,拷贝到block里面的就是指向变量的指针,所以我们就可以修改变量的值。

12. 造成内存泄漏的可能的原因?

第三方框架不正当使用。
block,delegate,NSTimer循环使用。
非oc对象的内存处理。
地图类处理。
大次数循环内存暴涨。

非oc对象的释放:
例如使用CGImageRelease(ref)方法释放内存;
CoreFoundation框架下的某些对象或者变量需要手动释放,c语言中malloc需要free;
地图类内存释放:
在使用完毕之后注意将地图及其相关代理释放,地图中大头针需正确复用,并使用完成之后清空标注;

13. 说一下线程之间的通信。

14. NSDictionary的实现原理是什么?

NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的,

15. 你们的App是如何处理本地数据安全的(比如用户名的密码)?

不要再移动端存储密码,要存储token,存的话也要先加密再存。
键盘缓存输入补充。应用快照缓存。

16. 什么是指针常量和常量指针?

17. 不借用第三个变量,如何交换两个变量的值?要求手动写出交换过程。

18. 说一下runloop和线程的关系。

19. 说一下autoreleasePool的使用场景

20. 说一下简单工厂模式,工厂模式以及抽象工厂模式?

21. UITableViewCell的卡顿你是怎么优化的?

1.避免cell的重新布局

cell的布局填充等操作 比较耗时,一般创建时就布局好
如可以将cell单独放到一个自定义类,初始化时就布局好

2.提前计算并缓存cell的属性及内容
3.减少cell中控件的数量

尽量使cell得布局大致相同,不同风格的cell可以使用不用的重用标识符,初始化时添加控件,

  1. 加载网络数据,下载图片,使用异步加载,并缓存
  2. 使用不透明视图。

不透明的视图可以极大地提高渲染的速度。因此如非必要,可以将table cell及其子视图的opaque属性设为YES(默认值)。

22. 说一下静态库和动态库之间的区别

静态库和动态库是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库;而动态库在程序编译时并不会被链接到目标代码中,只是在程序运行时才被载入,因为在程序运行期间还需要动态库的存在。

静态库:在链接时会被完整的复制到可执行文件中,被多次使用就会进行多次的拷贝,也就是说你有10个项目就要拷贝10次,静态库与 app 可执行文件 一起被加载到同一块代码区中。
动态库:链接的时候是不复制,动态库的代码是在可执行程序运行时才会被载入内存的,在编译过程中仅简单的引用,因此代码体积较小。系统只加载一次,多个程序会共用,在ios中也需要系统的支持。

静态库 好处:

模块化,分工合作,提高了代码的复用及核心技术的保密程度
避免少量改动经常导致大量的重复编译连接
也可以重用,注意不是共享使用

动态库 好处:

使用动态库,可以将最终可执行文件体积缩小,将整个应用程序分模块,团队合作,进行分工,影响比较小
使用动态库,多个应用程序共享内存中得同一份库文件,节省资源
使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。
应用插件化
软件版本实时模块升级

23. load方法和initalize方法有什么区别和共同点

  1. load和initialize都会在实例化对象之前调用,以main函数为分水岭,前者是在main函数之前,后者是在main函数之后。

  2. load和initialize方法都不会显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,load方法不会调用父类。

  3. load和initialize方法内部使用了锁,因此他们是线程安全的,实现时要尽可能简单,避免线程阻塞,不要再次使用锁。

  4. load方法常用来method swizzle,initialize常常用于初始化全局变量和静态变量.

24. NSNotificationCenter是在哪个线程发送的通知?

取决于发送通知时所在的线程,线程不安全

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

为什么都要在主线程中更新UI

GUI为了性能(不知道GUI的可以自己查一下),故意让你只能在一个线程里面操作的,多线程操作一个UI,很容易导致,或者极其容易导致反向加锁和死锁问题。其实不光是GUI,同样的道理在几乎所有编程领域里都是这样的,这背后是线程同步的开销问题。

通俗的讲显然两个线程不能同时draw,否则屏幕会花;不能同时insert map,否则内存会花;不能同时write buffer,否则文件会花。需要互斥,比如锁。结果就是同一时刻只有一个线程可以做UI。那么当两个线程互斥几率较大时,或者保证互斥的代码复杂时,选择其中一个长期持有其他发消息就是典型的解决方案。所以普遍的要求UI只能单线程。

26. 如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

遵守NSCopying协议,实现- (id)copyWithZone:(NSZone *)zone

- (void)setName:(NSString *)name
{
  if (_name != name) {
    _name = [name copy];
  }
}

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

@property的本质 = ivar (实例变量) + getter (取方法) + setter (存方法)

“属性”(property)有两大概念:实例变量(ivar)、存取方法(getter + setter)

ivar、 getter 、setter 是如何生成并添加到这个类中的

这是编译器自动合成的,通过@synthesize 关键字指定,若不指定,默认为@synthesize propertyName = _propertyName;若手动实现了getter/setter 方法,则不会自动合成。
现在编译器已经默认为我们添加了@synthesize propertyName = propertyName;因此不再手动添加了,除非你真的要改变成员变量名字。
生成getter方法时,会判断当前属性名是否有“
”,比如声明属性为@property(nonatomic,copy)NSString *_name;那么所生成的成员变量名就会变成“name”,如果我们要手动生成getter 方法,就要判断是否以“”开头了。

28. 单个viewController的生命周期

initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
loadView:加载view
viewDidLoad:view加载完毕
viewWillAppear:控制器的view将要显示
viewWillLayoutSubviews:控制器的view将要布局子控件
viewDidLayoutSubviews:控制器的view布局子控件完成
这期间系统可能会多次调用viewWillLayoutSubviews 、 viewDidLayoutSubviews 俩个方法
viewDidAppear:控制器的view完全显示
viewWillDisappear:控制器的view即将消失的时候
这期间系统也会调用viewWillLayoutSubviews 、viewDidLayoutSubviews 两个方法
viewDidDisappear:控制器的view完全消失的时候

29. Cocopods

让自己项目支持coocopods:
  1. 创建配置podspec文件。
  2. 创建tag,并推送到github(podspec会解析为podspec.json)。
  3. 验证podspec文件。
安装cocopods:

1,sudo gem install cocoapods
2,pod setup (https://github.com/CocoaPods/Specs所有库信息download到本地)

使用cocopods:

pod search 从本地的库中搜索相关库信息
pod install
pod update

原理:
  1. 它是将所有的依赖库都放到另一个名为 Pods 项目中
  2. Pods 项目最终会编译成一个名为 libPods.a 的文件,主项目只需要依赖这个 .a 文件即可。这样,依赖库源码管理工作都从主项目移到了 Pods 项目中。

核心组件:
深入理解 CocoaPods

CocoaPods是用 Ruby 写的,并由若干个 Ruby 包 (gems) 构成的。在解析整合过程中,最重要的几个 gems 分别是: CocoaPods/CocoaPods, CocoaPods/Core, 和 CocoaPods/Xcodeproj (是的,CocoaPods 是一个依赖管理工具 -- 利用依赖管理进行构建的!)。

pod install步骤 :

  1. 读取 Podfile 文件
  2. 版本控制和冲突
  3. 加载源文件
  4. 生成 Pods.xcodeproj
  5. 安装第三方库

30. SDWebImage:

主要加载的类:

  1. SDWebImageManager
  2. SDImageCache:memCache,diskCachePath
  3. SDWebImageDownloader:
    NSOperationQueue *downloadQueue,
    NSMutableDictionary *URLOperations;

31. static和const

你可能感兴趣的:(面试总结)