2018-07-30 面试题总结
1.UIView 与CALayer的关系
每个UIView内部都有一个CALayer在背后提供内容的绘制和显示,并且UIView的尺寸样式都是由内部的layer所提供;
在view显示的时候,UIView作为layer的CALayerDelegate,view的显示内容取决于内部的CALayer的display;
最明显的区别是view可以接受并处理事件,layer不可以
2.self super的区别于联系
self:是关键字,代表当前方法的调用者
如果是类方法,则代表当前类;如果是对象方法,则代表当前类的对象
super:编译器指令
其实不管是self还是super,真正调用的对象都是一样的,只是查找方法的位置不一样.self是从当前类结构中开始查找,super是从父类中查找,但方法真正的接受者都是当前类或者当前类的对象.
延伸思考:重写init方法为什么要用self=[super init]?
[super init]是面向对象的体现,初始化父类的一些属性;
将[super init]赋给self是为了防止父类的初始化方法release掉了self指向的空间并重新alloc了一块内存空间,这样的话[super init] 可能 alloc失败,导致返回为nil,这时就不再执行if条件语句了
3.@synthesize和@dynamic分别有什么作用?
@property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie view = _view;
@synthesize 如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
@dynamic告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可).假如一个属性被声明为 @dynamic view,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.view = someView,由于缺 setter 方法会导致程序崩溃;或者当运行到 someView = view 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
4.类扩展 extension,匿名分类,作用:为类添加一些私有成员和方法.
类别Category,也叫分类,作用如下:
a.可以将类的实现分散到多个不同文件或者多个不同框架中,方便代码管理
b.创建对私有方法的前向引用
c.向对象添加非正式协议
一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系.
5.OC的类可以多重继承么?可以实现多个接口吗?
oc不可以多继承,是单继承的;可以用协议protocol来实现多个接口,通过实现多个接口来完成多重继承.
6.用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
因为这些类型都有对应的可变类型的子类,他们之间可能进行赋值操作,为保证对象中的字符串值不会被无意中改动,应该在设置新属性值时拷贝一份.如果使用strong修饰,就有可能指向一个可变对象,如果这个可变对象在外部被修改了,就会影响该属性.
7.KVO的基本原理
a.kvo是基于runtime机制实现的
b.当某个类的属性对象第一次被观察时,系统就会在运行期动态的创建该类的一个派生类,在这个派生类中重写基类中任何被观察的属性的setter方法.派生类在被重写的setter方法中实现真正的通知机制
c.如果原类是person,那么生成的派生类为NSKVONotifying_Person
d.每个类对象中都有⼀个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从⽽在给被监控属性赋值时执行的是派生类的setter⽅方法
e.键值观察通知依赖于NSObject 的两个⽅方法: willChangeValueForKey: 和 didChangevlueForKey:;在⼀个被观察属性发⽣改变之前, willChangeValueForKey:一定会被调用,这就会记录旧的值。而当改变发生后, didChangeValueForKey:会被调⽤,继⽽而observeValueForKey:ofObject:change:context: 也会被调⽤
8.objc在向一个对象发送消息时,发生了什么?
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类的方法列表及父类的方法列表中寻找方法运行,然后在发送消息的时候,objc_msgsend方法不会返回值,所谓的返回内容都是具体调用时执行的.
9.runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了了runloop 么?⼦子线程呢?
runloop:字面意思就是跑圈,其实也就是一个循环跑圈,用来处理线程里边的事件和消息
runloop和线程的关系:每个线程如果想继续运行,不被释放,就必须有一个runloop来不停的跑圈,以来处理线程里面的事件和消息
主线程是默认开启一个runloop的,子线程默认没有开启
10.完整消息转发机制
第一步:对象收到无法解读的消息后,首先会调用方法(resolveInstanceMethod或者resolveClassMethod)进行询问是否有动态添加方法来处理,有就处理,没有就执行第二步
第二步:没有新增方法的话,就询问是否有别的类能帮忙处理(forwardingTargetForSelector),如果有结束,没有执行最后一步
第三步:调用forwardInvocation,若消息未能处理,则会调用- (void)doesNotRecognizeSelector:(SEL)aSelector,可以在该方法中做文章,避免crash掉.实际开发中,尽量不做处理,让其抛出异常
11.定时器
NSTimer: 两种创建方法 scheduledTimerWithTimeInterval和timerWithTimeInterval.前者自动启动,后者需要手动启动
CADisplayLink:基于屏幕刷新的周期,1/60秒,适合做UI的不停重绘
GCD:GCD定时器是dispatch_source_t类型的变量
12.性能优化https://www.jianshu.com/p/9220374d6f33
a.tableview优化 :重用标识符的使用;view不透明;减少subview的数量
b.UIImage加载图片性能优化:如果加载大图且仅使用一次,使用imageWithContentsOfFile
c.不要在viewwillappear中做耗时操作
d.不要阻塞主线程
e.Instrument优化
13.串行与并行/异步与同步? 并发与并行的区别
队列分为串行与并行;任务的执行分为同步和异步;队列只负责任务的调度,不负责任务的执行
区别:并发和并⾏是即相似⼜有区别的两个概念,并⾏是指两个或者多个事件在同一时刻发⽣;⽽并发是指两个或多个事件在同一时间间隔内发⽣。在多道程序环境下, 并发性是指在一段时间内宏观上有多个程序在同时运⾏,但在单处理理机系统中,每一 时刻却仅能有一道程序执⾏,故微观上这些程序只能是分时地交替执⾏。倘若在计算 机系统中有多个处理机,则这些可以并发执⾏的程序便便可被分配到多个处理理机上,实现并行执⾏,即利用每个处理理机来处理一个可并发执⾏的程序,这样,多个程序便以同时执⾏。
14.线程和进程
一个程序至少有一个进程,一个进程至少有一个线程,进程有自己独立的内存单元,线程是进程的基本组成单元.
15.谈谈多线程的理解?ios有几种方法实现多线程
好处:可以把占据时间长的程序中的任务放到后台去处理,提高程序的运行速度;
坏处:如果有大量的线程会影响性能,因为操作系统需要在他们之间切换;
16.内存管理原则
遵循“谁创建谁释放,谁引用谁管理”的机制,当创建或引用一个对象的时候,需要向他发送alloc、copy、retain消息,当释放该对象时需要发送release消息,当对象引用计数为0时,系统将释放该对象,这是OC的手动管理机制(MRC)。iOS5.0之后引用自动管理机制(ARC),管理机制与手动管理机制一样,只是不再需要调用retain、release、autorelease;它是编译时特性,当使用ARC时,在适当的位置插入release和autorelease;它引用strong和weak关键字,strong修饰的指针变量指向对象时,当指针指向新值或者指针不存在时,相关联的对象就会自动释放,而weak修饰的指针变量指向对象,当指针的拥有者指向新值或者不存在时weak修饰的指针会自动置为nil。
17.dispatch_barrier_async的作用是什么?
在并行队列中,为了保证某些任务的顺序,需要等待一些任务完成后才能继续进行,使⽤barrier 来等待之前任务完成,避免数据竞争等问题
18.如何⽤用GCD同步若⼲个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
使⽤用Dispatch Group追加block到Global Group Queue,这些block如果全部执行完毕,就会执行Main Dispatch Queue中的结束处理理的block.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ /*加载图⽚片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图⽚片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图⽚片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 合并图⽚片 });
19. isKindOfClass与isMemberOfClass的区别
isKindOfClass:确定一个对象是否是一个类的成员,或者是该类的子类成员
isMemberOfClass:只能确定一个对象是否是当前类的成员.
比如AString继承自NSString,那么如果用 [AString的成员 isKindOfClass:[NSString class]] 返回的是YES,使用isMemberOfClass返回的是NO。
20.IAP内购中虚拟货币导致审核⽆法通过的问题?
有的时候需要在app中使用虚拟货币,在我们的app中可以使⽤用虚拟货币进行购买道具 等,比如直播中的礼物,游戏中的道具等。
苹果对于虚拟货币是需要提成的,提成的额度为30%。所以对于这块的审核⽐比较严 格。⾸首先你们的购买的道具在ios端和安卓端是需要分开的。如果⼤家玩游戏的就会发现游戏的数据在两端是分开的。 ⽤户在安卓⼿机上购买的道具在ios上不能使用。因为这样也间接的影响了了苹果的收入。
另外就是在审核期间不能有可以兑换在appStore可购买的商品的任意活动或者 换码,这个也是苹果不允许的。因为这个也会影响苹果的收⼊。
另外就是可能有人会在苹果审核之后隐藏ipa⽀支付,此处提醒下,苹果会扫描你的app代码中是否有⽀支付宝,微信等关于支付的字段。使⽤开关加h5的⽅式可以通过审核,但是此处也有⻛险,风险就是⼀一旦被发现,可能的结果就是苹果直接封掉账号。app⽆法使⽤
21.野指针和空指针
只要一个对象被释放了,我们就称这个对象为僵尸对象(不能再使用的对象);当一个指针指向一个僵尸对象,我们就称这个指针为野指针;只要给一个野指针发送消息就会报错;
空指针:没有指向内存空间的指针(里面存的是nil,也就是0),给空指针发消息是没有任何反应的
22.atomic nonatomic
a.atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作
b.atomic所说的线程安全只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的
23.NSTimer 定期器准吗?为什么不准?如何优化的?
不准;
原因:
a.runloop的影响,如果加入的runloop中做了耗时操作,当前runloop的持续时间超过了定时器的间隔时间,那么下一次定时就会被延后;
b.runloop模式的影响
24.assign vs weak?
__block vc __weak?
assign适用于基本数据类型,weak适用于NSObject对象,并且是一个弱引用;assign其实也可以用来修饰对象,但是因为被assign修饰的对象在释放之后,指针的地址还是存在的,也就是说指针并没有被置为nil,如果在后续的内存分配中,刚好分到了这块地址,程序就会崩溃;而weak修饰的对象在释放之后,指针地址被置为nil.
延伸问题:实现weak后,为什么对象释放后会自动置为nil?
runtime对注册的类会进行布局,对于weak对象会放入一个哈希表中,用weak指向的对象的内存地址作为key,当此对象的引用计数为0的时候会dealloc.假如weak指向的对象内存地址是a,那么就会以a为键,在这个weak表中搜索,找到所有以a为键的weak对象,从而置为nil
25.为什么被__block修饰的变量可以在block内部被修改?
block不允许修改外部变量的值,这里所说的外部变量的值指的是栈中指针的内存地址,__block的作用其实就是在堆上创建一个指向栈变量的指针达到修改栈变量值的目的。
26. alloc init new
alloc:分配内存
init:初始化
new:代替上边两个函数,分配内存并初始化
alloc分配内存的时候使用了zone,他是给对象分配内存的时候,把关联的对象分配到一个相邻的内存区域内,以便于调用时耗费很好的内存,提升了程序处理速度
不推荐new:因为没办法定制其他初始化方法
27.initialize和load的区别
load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。
28.如何高性能的给UIImageView加个圆角?
a.用UIGraphics和贝塞尔曲线
+ (UIImage*)drawCircleImageWithImage:(UIImage*)image circleRect:(CGRect)rect {
//开启上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
//设置裁剪区域
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];//圆形
[pathaddClip];//添加剪切路径
//绘制图片
[imagedrawAtPoint:CGPointZero];
//获取图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
returnnewImage;
}
b.贝塞尔曲线和CAShapeLayer
- (void)corner{
CGRect bounds = self.bounds;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:UIRectCornerBottomLeft cornerRadii:CGSizeMake(20,20)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
maskLayer.path = maskPath.CGPath;
[self.layer addSublayer:maskLayer];
self.layer.mask = maskLayer;
}
29.block
在 Objective-C 语言中,一共有 3 种类型的 block:
_NSConcreteGlobalBlock 全局的静态 block,不会访问任何外部变量。
_NSConcreteStackBlock 保存在栈中的 block,当函数返回时会被销毁。
_NSConcreteMallocBlock 保存在堆中的 block,当引用计数为 0 时会被销毁。
30.加密解密
rsa加密算法 非对称!!!
aes加密算法 对称
31.TCP UDP?
都是网络传输层的协议,TCP提供可靠的数据连接,是面向连接的一对一通信;速度慢
udp提供不可靠的数据连接,是广播式通信,一对多的方式,不会对数据包的顺序,是否丢失进行校验,如果丢失了也不会重新发送,速度快
32.分类里为什么不能添加属性?
在分类里使用@property声明属性,只是将该属性添加到该类的属性列表,并声明了setter和getter方法,但是并没有生成相应的成员变量,也没有实现setter和getter方法,所以说分类不能添加属性.
33.struct和class的区别
struct是一种数据结构的实现体,默认的数据访问控制是public;而class作为对象的实现体,默认的成员变量访问控制是私有的。
34.APNS推送机制
a.应用程序的服务器端把要发送的消息/目的iPhone的标识打包发给APNS;
b.APNS在自身已经注册的push服务的iPhone列表中,查找相应标识的iPhone,并把消息发送到iPhone
c.iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出push弹窗
35.HTTPS和HTTP的区别
a.https协议需要到ca申请证书
b.http超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
c.两者端口不同,前者是443,后者是80
36.runtime
运行时机制:最主要的是消息机制,在编译阶段oc可以调用任何函数,即使该函数并未实现,只要生命过就不会报错,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用. 比如一个对象A调用一个函数[A test],首先runtime会将oc代码通过objc_msgSend转化成objc_msgSend(A,@selector(test));然后通过A对象的isa指针找到对应的类,在类中先去缓存中查找对应的函数,若没找到,就去方法列表中查找,若还是没找到就去父类中找,若能找到就加入到缓存中,供下次查找,并执行相应的函数,若父类中也没找到则crash.
37.信号量
就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程.
dispatch_semaphore信号量为基于计数器的一种多线程同步机制。用于解决在多个线程访问共有资源时候,会因为多线程的特性而引发数据出错的问题
38.响应式编程(Functional Reactive Programming:FRP)
响应式编程是一种和事件流有关的变成模式,关注导致状态值改变的行为事件,一系列事件组成了事件流. 一系列事件流导致属性发生变化的原因,响应式编程类似于设计模式里的观察者模式.
39.触摸事件的传递与响应
传递:从父控件传递到子控件,即UIApplication->UIWindow->寻找处理事件最合适的view,如果父控件不能接受触摸事件,那么子控件就不可能收到触摸事件
pointInside:withEvent:方法判断点在不在当前view上(方法调用者的坐标系上)如果返回YES,代表点在方法调用者的坐标系上;返回NO代表点不在方法调用者的坐标系上,那么方法调用者也就不能处理事件
事件的传递和响应的区别:事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件).
40.类方法和实例方法的本质区别
元类是类对象的类
类方法是在这个类的元类的方法列表中查找,实例方法是在这个对象的类的方法列表中查找
41.提升编译速度
a.将Debug Information Format改为DWARF
在工程对应Target的Build Settings中,找到Debug Information Format这一项,将Debug时的DWARF with dSYM file改为DWARF。
这一项设置的是是否将调试信息加入到可执行文件中,改为DWARF后,如果程序崩溃,将无法输出崩溃位置对应的函数堆栈,但由于Debug模式下可以在XCode中查看调试信息,所以改为DWARF影响并不大。这一项更改完之后,可以大幅提升编译速度
b.将Build Active Architecture Only改为Yes
在工程对应Target的Build Settings中,找到Build Active Architecture Only这一项,将Debug时的No改为Yes。
这一项设置的是是否仅编译当前架构的版本,如果为No,会编译所有架构的版本。需要注意的是,此选项在Release模式下必须为Yes,否则发布的ipa在部分设备上将不能运行。这一项更改完之后,可以显著提高编译速度
42.main函数
a.动态链接库 (系统framework)
b.ImageLoader加载可执行文件, 里边是被编译过的符号,代码等
c.runtime初始化 + load
43.autoreleasepool
自动释放池用来存放那些需要在稍后的某个时刻释放的对象.
autoreleasepool是在runloop即将进入时创建,以及即将休眠时销毁的.
当自动释放池被销毁的时候,在池中的对象会自动调用release方法来释放资源,销毁对象
44.http通信
a.请求 http协议规定:1个完整的有客服端发送给服务器的http请求中包含以下内容
请求行:包含了请求方法,请求资源路径,http协议版本 GET /MJServer/resources/images/1.jpg HTTP/1.1
请求头:包含了对客户端环境的描述,客户端请求的主机地址等信息,
Host: 192.168.1.105:8080 // 客户端想访问的服务器主机地址
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9) Firefox/30.0// 客户端的类型,客户端的软件环境
Accept: text/html, */*// 客户端所能接收的数据类型
Accept-Language: zh-cn // 客户端的语言环境
Accept-Encoding: gzip // 客户端支持的数据压缩格式
请求体:客户端发送给服务器的具体数据,比如文件数据
b.响应 客户端向服务器发送请求,服务器应当做出响应,即返回数据给客户端
状态行:包含了http协议版本,状态码,状态英文名称 HTTP/1.1 200 OK
响应头:包含了对服务器的描述,对返回数据的描述
Server: Apache-Coyote/1.1 // 服务器的类型
Content-Type: image/jpeg // 返回数据的类型
Content-Length: 56811 // 返回数据的长度
Date: Mon, 23 Jun 2014 12:54:52 GMT // 响应的时间
实体内容:服务器返回给客户端的具体数据,比如文件数据
45. JSPatch 的原理就是
JS传递字符串给OC,OC通过 Runtime 接口调用和替换OC方法
46.NSMethodSignature和NSInvocation配合用来进行最后的消息转发
NSMethodSignature对方法的参数、返回值类型进行封装,协同NSInvocation实现消息转发;用于获取参数的个数和方法的返回值
NSInvocation用来包装方法和对应的对象,它可以存储方法的名称,对应的对象,对应的参数,target. 对象设置的参数个数及类型和获取的返回值的类型要与创建对象时使用的NSMethodSignature对象代表的参数及返回值类型向一致,否则crash。