美文网首页
iOS 一些面试知识点总结

iOS 一些面试知识点总结

作者: Cary9396 | 来源:发表于2018-09-30 11:51 被阅读0次
一.SDWebImage如何实现的

1.入口:setImagewithURL:placeholderImage:options:先把placeholderImage显示,然后SDWebImageManager 根据 URL 开始处理图片。
2.进入 SDWebImageManagerdownloadWithURL:delegate:options:userInfo:交给 SDImageCache 从缓存查找图片是否已经下载。queryDiskCacheForKey:delegate:userInfo:
3.先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4.SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage:
到 UIImageView+WebCache 等前端展示图片。
5.如果内存缓存中没有,生成 NSInvocationOperation,添加到队列开始从硬盘查找图片是否已经缓存。
6.根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
7.如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中
(如果空闲内存过小,会先清空内存缓存)。
SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。
进而回调展示图片。
8.如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
9.共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
10.图片下载由 NSURLConnection或NSURLSession 来做,
实现相关 delegate 来判断图片下载中、下载完成和下载失败。
11.didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
12.didFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
13.图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
14.在主线程 notifyDelegateOnMainThreadWithInfo:
宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:
回调给 SDWebImageDownloader。
15.imageDownloader:didFinishWithImage:回调给 SDWebImageManager 告知图片下载完成。
16.通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
17.将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
18.SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。

  1. 也提供了 UIButton+WebCache 和MKAnnotationView+WebCache,方便使用。
    20.SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
二.tableView造成卡顿的原因:

1.最常用的就是cell的重用, 注册重用标识符

如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell;
如果有很多数据的时候,就会堆积很多cell。
如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell

2.避免cell的重新布局

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

3.提前计算并缓存cell的属性及内容

当我们创建cell的数据源方法时,编译器并不是先创建cell 再定cell的高度
而是先根据内容一次确定每一个cell的高度,高度确定后,再创建要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提前估算高度告诉编译器,编译器知道高度后,紧接着就会创建cell,这时再调用高度的具体计算方法,这样可以方式浪费时间去计算显示以外的cell

4.减少cell中控件的数量

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

5.不要使用ClearColor,无背景色,透明度也不要设置为0

渲染耗时比较长

6.使用局部更新

如果只是更新某组的话,使用reloadSection进行局部更新

7.加载网络数据,下载图片,使用异步加载,并缓存

8.少使用addView 给cell动态添加view

9.按需加载cell,cell滚动很快时,只加载范围内的cell

10.不要实现无用的代理方法,tableView只遵守两个协议

11.缓存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这两者同时存在才会出现“窜动”的bug。所以建议是:只要是固定行高就写预估行高来减少行高调用次数提升性能。如果是动态行高就不要写预估方法了,用一个行高的缓存字典来减少代码的调用次数即可

12.不要做多余的绘制工作。在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。例如上例中,就可以用
CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。

13.预渲染图像。当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;

14.使用正确的数据结构来存储数据。

三:objc在向一个对象发送消息时,发生了什么?

根据对象的isa指针找到类对象id,在查询类对象里面的methodLists方法函数列表,如果没有找到,再沿着superclass,寻找父类,再在父类的methodLists方法列表里面查询,最终找到SEL,根据id SEL确认IMP(指针函数),再发送消息。

四:什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?

当发送消息时,我们在自己的类对象的缓存和方法列表中都没有找到方法,并且在父类的类对象的缓存和方法列表中都没有找到方法时,这时候就会启动动态方法解析。
+(BOOL)resolveInstanceMethod:(SEL)sel;
+(BOOL)resolveClassMethod:(SEL)sel;
进行动态方法解析结束之后,会从头开始再进行消息发送这一步,如果在动态方法解析的时候有动态添加方法实现,那么就能找到方法实现并返回方法实现。

如果没有将进入消息转发。(消息转发通俗地讲就是本类没有能力去处理这个消息,那么就转发给其他的类,让其他类去处理。)

消息转发首先依赖于- (id)forwardingTargetForSelector:(SEL)aSelector这个方法,若是这个方法直接返回了一个消息转发对象,则会通过objc_msgSend()把这个消息转发给消息转发对象了。若是这个方法没有实现或者实现了但是返回值为空,则会跑去执行后面的- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector这个函数以及- (void)forwardInvocation:(NSInvocation *)anInvocation这个函数。
当来到- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector这个方法时,如果这个方法返回为空,那么走到这里直接结束方法调用,产生崩溃,而如果返回不为空,那么就会继续去调用- (void)forwardInvocation:(NSInvocation *)anInvocation这个方法,那么来到这个里面,我们就可以为所欲为,即使我们什么也不做,运行程序也不会崩溃了,我们可以在这个方法里面为方法指定新的调用者,也即是进行消息转发,也可以做一些其他的操作,都可以,这就是这样设计的一个好处,我们可以在这个方法里面做一切我们想做的。

五:runtime如何实现weak变量的自动置nil?

runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

六:能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

1、不能向编译后得到的类增加实例变量 2、能向运行时创建的类中添加实例变量。

分析:1. 编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表和 instance_size 实例变量的内存大小已经确定,runtime会调用 class_setvarlayout 或 class_setWeaklvarLayout 来处理strong weak 引用.所以不能向存在的类中添加实例变量。2. 运行时创建的类是可以添加实例变量,调用class_addIvar函数. 但是的在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上.

七:给类添加一个属性后,在类结构体里哪些元素会发生变化?

instance_size :实例的内存大小;objc_ivar_list *ivars:属性列表

八:KVO实现原理

基本原理:

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

九.C语音内存分配

栈区:存放函数的参数值、局部变量的值的等,由编译器自动分配释放,通常在函数执行结束后就释放了,其操作方式类似数据结构中的栈。栈内存分配运算内置于处理器的指令集,效率很高,但是分配的内存容量有限,比如iOS中栈区的大小是2M。

堆区:就是通过new、malloc、realloc分配的内存块,它们的释放编译器不去管,由我们的应用去释放,如果应用程序没有释放掉,操作系统会自动回收。分配方式类似于链表。

静态区:全局变量和静态变量的存储是放一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束时,由系统释放。

常量区:常量存储在这里,不允许修改的。

代码区:存放函数体的二进制代码。

十.block

block有三种类型:
全局块(_NSConcreteGlobalBlock)
栈块(_nsConcreteStackBlock)
堆块(_NSConcreteMallocBlock)

全局块存储在静态区,相当于oc中的单例;栈块存储在栈区,超出作用域则马上被销毁。堆块存储在堆区,是一个带引用计数的对象,需要自行管理其内存。

当block不访问外界变量时:此时为全局块。ARC MRC都是如此。
当block访问外界变量时:
MRC环境下:访问外界变量的block默认存储在栈区。
ARC环境下:访问外界变量的block默认存放在堆中,实际上是先放在栈区,ARC情况下自动又拷贝到堆区,自动释放。

复制到堆区的主要目的就是保存block的状态,延长其生命周期。因为block如果在栈上的话,其所属变量作用域结束,该block就被释放掉,block中的_block变量也同时被释放掉。为了解决这个问题,需要把block复制到堆里。

在block不是作为一个property的时候,可以在block里面直接使用self,比如UIView的animation动画block。
当block被声明为一个property的时候,需要在block里面使用weakSelf,来解决循环引用的问题。
当和并发执行相关的时候,当涉及异步的服务的时候,block可以在之后被执行,并且不会发生关于self是否存在的问题。

十一.@property

@property本质:
@property = ivar+getter+setter;
在我们属性定义完成后,系统会自动生成该属性的getter和setter方法,这个过程叫做自动合成。除了生成存取方法,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加上下划线,以此作为实例变量的名字。

@synthesize的作用就是如果你没有手动实现getter与setter方法,那么编译器就会自动为你加上这两个方法。
@dynamic的作用就是告诉编译器,getter与setter方法由用户自己实现,不自动生成。当然对于readonly的属性只需要提供getter即可。
如果都没有写@synthesize和@dynamic,那么默认的就是@synthesize var = _var。

1、当我们指定了成员变量的名称(指定为带下划线的myName),就会生成指定的成员变量。如果代码中存在带下划线的name,就不会在生成了。2、如果是@synthesize name;还会生成一个名称为带下划线的name成员变量,也就是说如果没有指定成员变量的名称会自动生成一个属性同名的成员变量。3、如果是@synthesize name = _name; 就不会生成成员变量了。

什么时候不会使用自动合成?

同时重写了setter和getter时。
重写了只读属性的getter时。
使用了@dynamic时。
在@protocol中定义的所有属性。
在category中定义的所有属性。
重载的属性。

注意点:

在category中使用@property也是只会生成getter和setter方法的声明,如果真的需要给category增加属性的实现,需要借助于运行时的两个函数:objc_setAssociatedObject和objc_getAssociatedObject。
在protocol中使用property只会生成setter和getter方法声明,使用属性的目的是希望遵守我协议的对象能够实现该属性。

十二.具体分析WKWebView的优劣势

1.内存占用是UIWebView的1/4~1/3
2.页面加载速度有提升,有的文章说它的加载速度比UIWebView提升了一倍左右。
3.更为细致地拆分了 UIWebViewDelegate 中的方法
4.自带进度条。不需要像UIWebView一样自己做假进度条(通过NJKWebViewProgress和双层代理技术实现),技术复杂度和代码量,根贴近实际加载进度优化好的多。
5.允许JavaScript的Nitro库加载并使用(UIWebView中限制)
6.可以和js直接互调函数,不像UIWebView需要第三方库WebViewJavascriptBridge来协助处理和js的交互。
7.不支持页面缓存,需要自己注入cookie,而UIWebView是自动注入cookie。
8.无法发送POST参数问题

相关文章

网友评论

      本文标题:iOS 一些面试知识点总结

      本文链接:https://www.haomeiwen.com/subject/qpwbnftx.html