面试题整理
1、id 声明的对象有什么特性?
id
声明的对象具有运行时的特性,即可以指向任意类型的Objective-C对象(与C中的void*
万能指针相似)。
2、Object-C的类可以多重继承么? 可以实现多个接口么?
Object-C的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;
3、category是什么? 重写一个类的方式用继承好还是分类好? 为什么?
Category是类别,一般情况用分类好; 用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
4、#import跟#include有什么区别,@class呢? #import<>跟#import“”有什么区别?
#import
是Objective-C导入头文件的关键字,#include
是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入,相当于#include和#pragma once;@class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含;#import<>
用来包含系统的头文件,#import“”
用来包含用户头文件。
5、对于语句NSString *obj = [[NSData alloc] init]; obj在编译时和运行时分别是什么类型的对象?
编译时是NSString的类型;运行时是NSData类型的对象
6、什么情况下使用关键字weak和assign,两者有何不同?
他们常用在基本类型属性,比如BOOL,int等,还有就是delegate。
在使用delegate时,需要注意,非ARC时是使用assign,但到了ARC时代,都建议使用weak,这样更安全。
不管是在非ARC还是ARC,使用assign时,都需要注意释放
assign指针赋值, 不对引用计数操作, 使用之后如果没有置为nil, 可能就会产生野指针; 而weak一旦不进行使用后, 永远不会使用了, 就不会产生野指针。
assigin可以用于非OC对象,但是weak必须用于OC对象。
7、KVO的实现原理?
- KVO是基于runtime机制实现的
- 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
- 如果原类为Person,那么生成的派生类名为NSKVONotifying_Person
- 每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
- 键值观察通知依赖于NSObject的两个方法:willChangeValueForKey:和didChangeValueForKey:;在一个被观察属性发生改变之前;willChangeValueForKey:一定会被调用,这就会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而observeValueForKey:ofObject:change:context:也会被调用。
- 补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类
参考资料1
参考资料2
8、loadView方法什么时候调用?一般在此方法里面做什么操作?
当控制器ViewController要展示的view为nil的时候调用该方法,ViewController会自动调用loadView方法来初始化一个UIView并赋值给view属性
9、请描述一下SDWebImage内部实现的原理
首先介绍一下 这个库的作用
1、UIImageView (WebCache)类别,入口封装,实现读取图片完成后的回调
2、SDWebImageManager,对图片进行管理的中转站,记录那些图片正在读取。
向下层读取Cache(调用SDImageCache),或者向网络读取对象(调用SDWebImageDownloader)。
实现SDImageCache和SDWebImageDownloader的回调。
3、SDImageCache,根据URL的MD5摘要对图片进行存储和读取(实现存在内存中或者存在硬盘上两种实现)
实现图片和内存清理工作。
4、SDWebImageDownloader,根据URL向网络读取数据(实现部分读取和全部读取后再通知回调两种方式)
SDWebImage实现原理
1.sd_setImageWithURL:url 先把默认图片显示出来
2.SDWebImageManager-downloadImageWithURL 从内存图片缓存中查找是否已经有图片queryDiskCacheForKey
3.imageFromMemoryCacheForKey 先从缓存中查找
4.如果有则展示图片
5.如果缓存中没有 生成NSInvocationOperation 添加到队列queryDiskCacheForKey
从硬盘查找是否已经有图片
6.从硬盘缓存目录读取文件 这一步是在NSOperator 进行操作
回到主线程进行结果回调 notifyDelegate
7.如果读取到了图片 将图片添加到内存缓存中 SDImageCacheDelegate回调imageCache:didFindImage:forKey:userinfo:
8.如果从硬盘目录中读取不到图片 说明图片不存在 需要下载图片
imageCache:didNotFindImage:forKey:userinfo:
9.重新生成一个下载器 SDWebImageDownloader开始下载图片
10.图片下载由NSURLConnection来做 实现相关delegate 来判断图片下载状态
//下面是下载过程
11.connection:didReceiveData:中利用ImageIO作了按图片进度加载效果
12.connectionDidFinishLoading:数据下载完成后交给SDWebImageDecoder做图片的解码处理
13.图片解码处理在一个NSOperationQueue完成不会拖慢主线程UI
14.在主线程notifiyDelegateOnMainThreadWithInfo 宣告解码完成
imageDecoder:didFinishDecodingImage:userInfo回调给SDWebImageDownloader
15.imageDownloder:didFinishWithImage:回调给SDWebImageManage 告知图片下载完成
16.通知所有的downloadDelegates下载完成,回调给需要的地方展示图片
17.将图片保存到SDImageCache中,内存缓存和硬盘缓存同时保存
写文件到硬盘 可以单独在NSInvocationOperation中完成 避免拖慢主线程
18.SDImageCache 在初始化的时候会注释一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期的图片
19.SDWebImage 也提供了UIButton+WebCache 和MKAnnotioinView+WebCache方便使用
20.SDWebImagePrefetcher可以预先下载图片 方便以后使用
10、线程间怎么通信?
线程间通信:
在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
线程间通信的体现:
1个线程传递数据给另1个线程
在1个线程中执行完特定任务后,转到另1个线程继续执行任务
线程间通信常用方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 1.耗时操作处理
dispatch_async(dispatch_get_main_queue(), ^{
// 2.回到主线程更新页面和数据
});
});
11、iOS中的数据持久化方案有哪些?
plist文件(属性列表)、preference(偏好设置NSUserDefaults,用于存储配置信息)、NSKeyedArchiver(归档)、SQList、CoreData
12、描述一下iOS SDK中如何实现MVC的开发模式
MVC是模型、视图、控制器开发模式,对于iOS SDK,所有的View都是视图层的,它应该独立于模型层,由视图器来控制。所有的用户数据都是模型层,它应该独立于视图。所有的ViewController都是视图器,由它负责控制视图,访问模型数据。
13、请用简单的代码展示@protocol的定义及实现.
#warning代理第一步:声明协议
@protocol MarryMe
- (void)makeMoney;
@end
#warning代理第二步:声明代理
@property(nonatomic, weak) id myDeleget;
.m文件中
#warning代理第三步:代理人执行协议方法
[self.myDeleget makeMoney];
代理人.m文件中
#warning代理第四步:签订协议
@interface Boy : NSObject
Girl *girl = [[Girl alloc] init];
#warning代理第五步:成为代理人
girl.myDeleget = self;
[girl getMessage:message];
#warning协议代理第六步:实现协议方法
- (void)makeMoney {
NSLog(@"=");
}
14、iOS中delegate代理对象使用weak和assign哪个更好?
使用
assign
声明一个delegate,那么即便delegate指向的对象销毁了,delegate中依然会保存之前对象的地址, 即delegate成为了一个野指针;而使用weak
,则不会有上述问题,当delegate指向的对象销毁后,delegate = nil。所以建议使用weak。资料
15、解释一下iOS内存管理机制和规则
iOS内存管理机制的原理是
引用计数
,引用计数
是一个简单而有效的管理对象生命周期的方式。当我们创建一个新对象的时候,他的引用计数为1,当有一个新的指针指向这个对象,我们将其引用计数加1,当某个指针不再指向这个对象时,我们将其引用计数减1,当对象的引用计数为0时,说明这个对象不再被任何指针指向了,这个时候我们就可以将对象销毁,回收内存。
参考资料1
参考资料2
参考资料3
16、define 和 const常量有什么区别?
const
为常量值,define
为字符串替换。
-
const
在编译阶段使用;define
在预编译阶段使用。 -
const
有数据类型,做安全检查;define
无类型,不做安全检查。 -
const
可以调试;define
不能调试。 -
const
占用一份内存;define
不占用内存,但是会多处进行字符串替换
参考资料
17、iOS如何避免内测泄漏?怎么调试?
18、runloop是什么?它和线程有什么关系?RunLoop内部是怎么实现的?
Run Loop是一让线程能随时处理事件但不退出的机制;
- RunLoop 的作用就是来管理线程的,当线程的 RunLoop 开启后,线程就会在执行完任务后,处于休眠状态,随时等待接受新的任务,而不是退出;
- 只有主线程的RunLoop是默认开启的,所以程序在开启后,会一直运行,不会退出。其他线程的RunLoop 如果需要开启,就手动开启;
- //猜想runloop内部是如何实现的?
1、有一个判断循环的条件,满足条件,就一直循环
2、线程得到唤醒事件被唤醒,事件处理完毕以后,回到睡眠状态,等待下次唤醒
参考资料
19、常用的设计模式有哪些?说两个iOS开发中常见的。
20、说说UIView触摸事件的传递;不能接受触摸事件的原因可能有哪些?如何找出最合适处理事件的控件?
21、copy和strong关键字的区别?
strong
与copy
都会使引用计数加1,但strong
是两个指针指向同一个内存地址,copy
会在内存里拷贝一份对象,两个指针指向不同的内存地址
22、如何高性能的给UIImageView加个圆角?
23、UIView和CALayer的关系?
写在最后
如有错误,请指教!