前言:总结了一些比较经典的iOS面试题,和找工作的你共同进步互勉(其实看这些面试题,也是对自己知识掌握程度的一个判断,一个查漏补缺的过程,只要技术掌握好了,不愁找不到工作)
查看目录:
1. 描述下SDWebImage里面给UIImageView加载图片的逻辑
2. 请简述UITableView的复用机制
3. 控制器的生命周期
4. 你是怎么封装一个view的
5. 如何进行iOS6、7的适配
6. 用过Core Data 或者 SQLite吗?读写是分线程的吗?遇到过死锁没?如何解决的?
7. iOS 怎么存储数据
8. 项目上线的流程是什么
9. 说几个常见的SQlite语句把,比如说增删查改
10. 沙盒里面都有什么,具体一点
11. 推送通知分为两种,一个是本地推送,一个是远程推送
12. 为什么 UIScrollView 的滚动会导致 NSTimer 失效?
13. 为什么要学习编程,编程对你而言的乐趣在哪儿?
14. 简述类目category优点和缺点
15. 默认的引用方式是强引用,但上面说了有时我们还得使用弱引用,那是什么情况呢?
16. **#include与**#import的区别、**#import **与@class 的区别
17. 堆和栈的区别
18. iOS 界面卡顿原因
19. iOS面试题目二
20. iOS面试题目一
21. UITableView优化技巧
22.某公司笔试题
23.说说pushViewController 和 PresentViewController的区别
24.简要说说KVC的实现原理
25.为什么要使用单例
26.如何访问类中私有方法
NO.1 描述下SDWebImage里面给UIImageView加载图片的逻辑
- SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最常用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后在替换占位图片
- 加载图片的过程大致如下:
- 首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 作为数据的索引先在内存中寻找是否有对应的缓存
- 如果缓存未找到就会利用通过MD5处理过的key来继续在磁盘中查询对应的数据, 如果找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
- 如果在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
- 下载后的图片会加入缓存中,并写入磁盘中
- 整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来
NO.2 请简述UITableView的复用机制
- 每次创建cell的时候通过dequeueReusableCellWithIdentifier:方法创建cell,它先到缓存池中找指定标识的cell,如果没有就直接返回nil
- 如果没有找到指定标识的cell,那么会通过initWithStyle:reuseIdentifier:创建一个cell
- 当cell离开界面就会被放到缓存池中,以供下次复用
NO.3 控制器的生命周期
#就是问的view的生命周期,下面已经按方法执行顺序进行了排序
// 自定义控制器view,这个方法只有实现了才会执行
- (void)loadView{
self.view = [[UIView alloc] init]; self.view.backgroundColor = [UIColor orangeColor];
}
// view是懒加载,只要view加载完毕就调用这个方法
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(@"%s",__func__);
}
// view即将显示
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; NSLog(@"%s",__func__);
}
// view即将开始布局子控件
- (void)viewWillLayoutSubviews{ [super viewWillLayoutSubviews]; NSLog(@"%s",__func__);
}
// view已经完成子控件的布局
- (void)viewDidLayoutSubviews{ [super viewDidLayoutSubviews]; NSLog(@"%s",__func__);
}
// view已经出现
- (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; NSLog(@"%s",__func__);
}
// view即将消失
- (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated];
NSLog(@"%s",__func__);
}
// view已经消失
- (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; NSLog(@"%s",__func__);
}
// 收到内存警告
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; NSLog(@"%s",__func__); }
// 方法已过期,即将销毁view- (void)viewWillUnload { }
// 方法已过期,已经销毁view- (void)viewDidUnload {}
NO.4 你是怎么封装一个view的
可以通过纯代码或者xib的方式来封装子控件
建立一个跟view相关的模型,然后将模型数据传给view,通过模型上的数据给view的子控件赋值
/** * 纯代码初始化控件时一定会走这个方法 */
- (instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
[self setup];
}
return self;
}
/** * 通过xib初始化控件时一定会走这个方法 */
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder])
{
[self setup];
}
return self;
}
- (void)setup
{
// 初始化代码
}
NO.5 如何进行iOS6、7的适配
通过判断版本来控制,来执行响应的代码
功能适配:保证同一个功能在6、7上都能用
UI适配:保证各自的显示风格
// iOS版本为7.0以上(包含7.0)
#define iOS7 ([[UIDevice currentDevice].systemVersion doubleValue]>=7.0)
NO.6 用过Core Data 或者 SQLite吗?读写是分线程的吗?遇到过死锁没?如何解决的?
- 用过SQLite,使用FMDB框架
- 丢给FMDatabaseQueue 或者 添加互斥锁(NSLock/@synchronized(锁对象))
NO.7 iOS 怎么存储数据
iOS 数据存储的常用方式
NO.8 项目上线的流程是什么
iOS APP上架流程
NO.9 说几个常见的SQlite语句把,比如说增删查改
SQLite 初学
NO.10 沙盒里面都有什么,具体一点
iOS沙盒机制介绍
NO.11 推送通知分为两种,一个是本地推送,一个是远程推送
本地推送:不需要联网也可以推送,是开发人员在APP内设定特定的时间来提醒用户干什么
远程推送:需要联网,用户的设备会于苹果APNS服务器形成一个长连接,
用户设备会发送uuid和Bundle idenidentifier给苹果服务器,
苹果服务器会加密生成一个deviceToken给用户设备,
然后设备会将deviceToken发送给APP的服务器,
服务器会将deviceToken存进他们的数据库,
这时候如果有人发送消息给我,服务器端就会去查询我的deviceToken,
然后将deviceToken和要发送的信息发送给苹果服务器,
苹果服务器通过deviceToken找到我的设备并将消息推送到我的设备上,
这里还有个情况是如果APP在线,
那么APP服务器会于APP产生一个长连接,
这时候APPF服务器会直接通过deviceToken将消息推送到设备上
NO.12 为什么 UIScrollView 的滚动会导致 NSTimer 失效?
- 定时器里面有个runoop mode,一般定时器是运行在defaultmode上但是如果滑动了这个页面,主线程runloop会转到UITrackingRunLoopMode中,这时候就不能处理定时器了,造成定时器失效,原因就是runroop mode选错了,解决办法有2个,一个是更改mode为NSRunLoopCommonModes(无论runloop运行在哪个mode,都能运行),还有种办法是切换到主线程来更新UI界面的刷新
NO.13 为什么要学习编程,编程对你而言的乐趣在哪儿?
- 这个问题面试官想考察的是你对编程的认知是什么,编程对于你而言是什么,你对编程的态度如何,你是否是对这个编程有浓厚的兴趣而不是纯粹的只是因为这行赚钱多才做这行
- 参考答案:为什么学习编程这边可以介绍下你是如何进入这行的,乐趣对于我而言有以下几点吧.
- 看着代码一行行从指下敲出,然后慢慢构建成一个完整的APP的那种愉悦感.
- 花费长时间去解决一个BUG,当最终解决掉这个BUG后那种酣畅淋漓的感觉.
- 有人问你问题,然后你帮忙解决以后,那种满足的感觉.
- 你可以和懂技术的人聊天,就像大学时候大家一起聊LOL一样
NO.14 简述类目category优点和缺点
优点:
- 不需要通过增加子类而增加现有类的行为(方法),且类目中的方法与原始类方法基本没有区别;
- 通过类目可以将庞大一个类的方法进行划分,从而便于代码的日后的维护、更新以及提高代码的阅读性;
缺点:
- 无法向类目添加实例变量,如果需要添加实例变量,只能通过定义子类的方式;
- 类目中的方法与原始类以及父类方法相比具有更高优先级,如果覆盖父类的方法,可能导致super消息的断裂。因此,最好不要覆盖原始类中的方法。
NO.15 默认的引用方式是强引用,但上面说了有时我们还得使用弱引用,那是什么情况呢?
- 强引用循环:A对象强引用了B对象,B对象也强引用了A。因为都是强引用,也就是无论是A是B都要在对方的引用断了后才能销毁,但要断了引用,就必须对方对象销毁。就会出现这种僵局,为了避免出现这种情况,就应该有一个对象“示弱”,使其为“弱引用”。
NO.16 #include与#import的区别、**#import **与@class 的区别
- include 和#import其效果相同,都是查询类中定义的行为(方法);
- import不会引起交叉编译,确保头文件只会被导入一次;
- @class 的表明,只定 义了类的名称,而具体类的行为是未知的,一般用于.h 文件;
- @class 比#import 编译效率更高。
- 此外@class 和#import 的主要区别在于解决引用死锁的问题。
NO.17 堆和栈的区别
- 栈区(stack)由编译器自动分配释放 ,存放方法(函数)的参数值, 局部变量的值等,栈是向低地址扩展的数据结构,是一块连续的内存的区域。即栈顶的地址和栈的最大容量是系统预先规定好的。
- 堆区(heap)一般由程序员分配释放, 若程序员不释放,程序结束时由OS回收,向高地址扩展的数据结构,是不连续的内存区域,从而堆获得的空间比较灵活。
- 碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出.
- 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
- 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。
全局区(静态区)(static),全局变量和静态变量的存储是放在一块 的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。 - 文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。
- 程序代码区—存放函数体的二进制代码
NO.18 iOS 界面卡顿原因
iOS 界面卡顿原因
NO.19 iOS面试题目二
iOS面试题目二
NO.20 iOS面试题目一
iOS面试题目一
NO.21 UITableView优化技巧
UITableView优化技巧
NO.22 某公司笔试题
标哥讲笔试题
NO.23 说说pushViewController 和 PresentViewController的区别
pushViewController:每push出来一个控制器都加入到导航控制器的管理之中,导航控制器相当于一个容器,里面放着所有push出来的控制器,最先push出来的控制器放在容器的底部,依次加入,当执行pop方法时,后加入容器中的控制器先弹出。 总结就是后进先出
presentViewController:只是将弹出控制器的View覆盖到当前控制器的View上
NO.24 简要说说KVC的实现原理
KVC的简单实现原理:
setValue:forKey方法:
(1)程序优先考虑调用属性的setter方法完成设置
(2)如果该类没有setter方法,KVC机制会搜索该类名为_name的成员变量,无论成员变量是在类接口部分定义,还是在类实现部分定义的,也无论用哪个访问控制符修饰,KVC的底层代码实际上就是对_name成员变量赋值
//这也是我们为什么能通过KVC设置textField的提示文字的颜色
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
//因为对于textField来说, _placeholderLabel.textColor这个成员变量是被隐藏在类实现部分的(猜测),所以我们只能通过KVC赋值
(3)如果该类以上两个都找不到,KVC会搜索该类名为name的成员变量,无论成员变量是在类接口部分定义,还是在类实现部分定义的,也无论用哪个访问控制符修饰,这个KVC代码实际就是返回name成员变量的值
(4)如果以上都找不到,系统会执行该对象的valueforUndefinedKey方法
默认的valueforUndefinedKey:方法实现就是引发一个异常,这个异常将会导致程序因为异常结束
NO.25 为什么要使用单例
在ARC出来之前。Xcode用MRC管理内存。 但是在ARC出来后,我们在很多地方创建对象,
alloc init 分配内存空间,初始化。
对于一些需要重复创建的对象,比如工具管理类,网络封装请求工具类。
我们就可以创建为单例。 节约内存空间,防止内存泄露。
NO.26 如何访问类中私有方法
1.使用performSelector:来调用
2.使用类别的向前引用来调用
//举例:
//创建一个测试类
@interface Test : NSObject
//定义一个共有方法
-(void)publicMethod;
@end
@implementation Test
//定义一个共有方法
-(void)publicMethod{
NSLog(@"这是一个公共方法");
}
//没有方法声明的私有方法
-(void)privateMethod{
NSLog(@"我是一个私有方法,只在本类实现中调用");
}
//演示:
//导入头文件
#import "Test.h"
#import "Test+TestMethod.h"
- (void)viewDidLoad {
[super viewDidLoad];
//创建实例
Test *test = [[Test alloc]init];
//调用公共方法
[test publicMethod];
//调用私有方法
//[test privateMethod];
//在没有使用类别之前,编译会报错误:在Test类的声明中找不到这个方法
//No visible @interface for 'Test' declares the selector 'privateMethod'
//接下来我们使用类别来处理
[test privateMethod];
}
//Test类别:
//给Test类搞一个类扩展(Category)
@interface Test (TestMethod)
//我们怎么解决私有方法不能调用的问题
//其实只需要在类扩展的声明部分将私有方法进行声明就好
-(void)privateMethod;
@end