iOS面试建议

定时器相关的问题

  • 定时器的模式问题是被问的最多的

UIScrollView(UITableView) 拖动时执行的是 UITrackingRunLoopMode,会导致暂停定时器,等恢复为 NSDefaultRunLoopMode 时才恢复定时器。
所以如果需要定时器在UIScrollView 拖动时也不影响的话,建议添加到UITrackingRunLoopModeNSRunLoopCommonModes 中:

NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode: UITrackingRunLoopMode]; ///< 或者 NSRunLoopCommonModes
  • 自动添加到当前NSRunLoop的方式,这种方式会导致滑动过程中定时器失效的问题,解决方式就是用(1)的方式设定NSRunLoop模式
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(test) userInfo:nil repeats:YES];
  • 定时器相关API的含义,没用过的可能会误解
[myTimer invalidate]; // 废弃定时器 ,是永久的停止,移除定时器对象
[myTimer setFireDate:[NSDate distantFuture]]; // 关闭定时器;遥远的将来才能触发,功能相当于关闭
[myTimer setFireDate:[NSDate distantPast]]; // 开启定时器;很久很久以前就触发了,功能相当于开启
  • GCD中的延时执行,相当于定时效果
dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(timer, dispatch_get_main_queue(), ^(void) {
    NSLog(@"GCD-----%@",[NSThread currentThread]);
});
  • NSRunLoop结合起来用,定时器是一种中断源,知道这个的,基本上做得比较底层了,在实际工作中,还没怎么遇到
@property (nonatomic ,strong)dispatch_source_t timer;//  注意:此处应该使用强引用 strong
{
    //0.创建队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //1.创建GCD中的定时器
    /*
     第一个参数:创建source的类型 DISPATCH_SOURCE_TYPE_TIMER:定时器
     第二个参数:0
     第三个参数:0
     第四个参数:队列
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    //2.设置时间等
    /*
     第一个参数:定时器对象
     第二个参数:DISPATCH_TIME_NOW 表示从现在开始计时
     第三个参数:间隔时间 GCD里面的时间最小单位为 纳秒
     第四个参数:精准度(表示允许的误差,0表示绝对精准)
     */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //3.要调用的任务
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"GCD-----%@",[NSThread currentThread]);
    });

    //4.开始执行
    dispatch_resume(timer);

    //
    self.timer = timer;
}

此处注意一定要强引用定时器 ,否则定时器执行到}后将会被释放,无定时效果。
GCD定时器时间非常精准,最小的定时时间可以达到1纳秒,所以用在非常精确的定时场合。

多线程相关问题

  • 串行队列和并行队列概念,同步执行和异步执行概念
NSLog(@"开始");
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"进行中");
});
NSLog(@"结束");

这段代码:只会打印第一句:开始,然后主线程就卡死了。
能回答这个问题并作出解释,说明对这个问题了解比较透彻了。

相对基础一点的问题:比较容易混淆
(1)dispatch_queue_t queue = dispatch_queue_create ( "com.dispatch.serial" , DISPATCH_QUEUE_SERIAL );是串行队列还是并行队列?
dispatch_queue_t queue = dispatch_queue_create ( "com.dispatch.serial" , DISPATCH_QUEUE_CONCURRENT );是串行队列还是并行队列?
(2)dispatch_get_main_queue()是串行的还是并行的?
(3)dispatch_get_global_queue()是串行的还是并行的?
(4)dispatch_sync()是同步执行还是异步执行?
(5)dispatch_async()是同步执行还是异步执行?

  • n个异步任务都执行完毕后再执行下一步的场景

图片很大,需要分3次下载,然后合并才能用,应该怎么做?

通过dispatch_group_t来实现,将每部分图片下载请求放入到Group中,将合并图片的操作放在dispatch_group_notify中实现。

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, ^{ /*下载图片Part1 */ });
dispatch_group_async(group, queue, ^{ /*下载图片Part2 */ });
dispatch_group_async(group, queue, ^{ /*下载图片Part3 */ }); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 合并图片
});
  • 任务间依赖的场景

有3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。如何实现?

//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下载图片");
}];

//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"打水印");
}];

//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"上传图片");
}];

//4.设置依赖
[operation2 addDependency:operation1];      //任务二依赖任务一
[operation3 addDependency:operation2];      //任务三依赖任务二

//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
  • iOS中多线程技术的种类以及选择

(1)NSOperation & NSOperationQueue :是对GCD的对象化封装,并且有cancel功能,推荐使用
(2)CGD:至少要知道模板型使用方式

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{   
    // 耗时的操作   
    dispatch_async(dispatch_get_main_queue(), ^{   
        // 更新界面   
    });   
});

(3)NSThread:控制力更强一点,比较底层了,用得不多。一些常用的方法可以问问,比如start,cancel,sleepForTimeInterval等等。
(4)NSObject就有的方法performSelectorInBackground,swift中这个方法被取消了
(5)pthread,知道这个的,c应该不错

  • 线程同步方式,在实际编程中遇到不多,但是概念比较重要

(1)NSLock方式

   [xxxlock lock] //上锁

  同步代码块

  [xxxlock unlock]//解锁

(2)NSCondition方式

   [xxxCondition lock] //上锁

  同步代码块

  [xxxCondition unlock]//解锁

(3)@synchronized( 同一对象) 一般是self;这个使用方便,但是很耗资源

   @synchronized(self){

  线程执行代码;

  }

(4)OSSpinLock自旋锁,atomic关键字,GCD串行队列,循环锁NSRecursiveLockpthreadmutex,信号量等等

本地存储,本地缓存

  • 沙盒的目录结构,使用场合
    Application:存放程序源文件,上架前经过数字签名,上架后不可修改
    Documents: 保存应⽤运行时生成的需要持久化的数据,iTunes同步设备时会备份该目 录。例如,游戏应用可将游戏存档保存在该目录
    tmp: 保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用 没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时 不会备份该目录
    Library/Caches: 保存应用运行时⽣成的需要持久化的数据,iTunes同步设备时不会备份 该目录。⼀一般存储体积大、不需要备份的非重要数据,比如网络数据缓存存储到Caches
    Library/Preference: 保存应用的所有偏好设置,如iOS的Settings(设置) 应⽤会在该目录中查找应⽤的设置信息。iTunes同步设备时会备份该目录

  • 数据持久化方案
    (1)plist属性列表存储(如NSUserDefaults
    (2)文件存储(如二进制数据写入文件存储,通过NSFileManager来操作将下载起来的二进制数据写入文件中存储)
    (3)NSKeydeArchiver归档存储, === 这个要实现NSCoding协议,model要实现哪两个函数?这点在实际使用中也可能忘记
    (4)数据库SQLite3存储(如FMDB、Core Data)=== 直接用SQL语句和CoreData的区别,CoreData的“坑”
    (5)KeyChain,用户名密码 === 代码怎么实现?需要导入哪几个系统framework

  • SDWebImage的原理,或者其他图片缓存库
    (1)从内存中(字典)找图片(当这个图片在本次程序加载过),找到直接使用;
    (2)从沙盒中找,找到直接使用,缓存到内存。
    (3)从网络上获取,使用,缓存到内存,缓存到沙盒。

  • YYCache的原理,或者其他缓存库的原理
    (1)内存缓存
    (2)硬盘缓存
    (3)key-value的方式,支持对象
    (4)本质还是sqlite数据库和NSFile文件系统;20K阈值,内容少用数据库,内容多用文件,综合性能最高
    (5)更新逻辑:设定时间和体积。队列方式,最新使用的,新加入,需要优先的放队列首部。更新时,先从队列尾部删除

  • NSURLCache、NSURLRequest本身的缓存机制,AFNetworking用的是这一套。==== 这个用得不多,了解这些细节的不多,比较难

(1)获得全局缓存对象(没必要手动创建)

NSURLCache *cache = [NSURLCache sharedURLCache]; 

(2)设置内存缓存的最大容量(字节为单位,默认为512KB)

- (void)setMemoryCapacity:(NSUInteger)memoryCapacity;

(3)设置硬盘缓存的最大容量(字节为单位,默认为10M)

- (void)setDiskCapacity:(NSUInteger)diskCapacity;

(4)硬盘缓存的位置:沙盒/Library/Caches

(5)取得某个请求的缓存

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; 

(6)清除某个请求的缓存

- (void)removeCachedResponseForRequest:(NSURLRequest *)request;

(7)清除所有的缓存

- (void)removeAllCachedResponses;
NSURLRequestUseProtocolCachePolicy // 默认的缓存策略(取决于协议)

NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求

NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未实现

NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求

NSURLRequestReturnCacheDataElseLoad// 有缓存就用缓存,没有缓存就重新请求

NSURLRequestReturnCacheDataDontLoad// 有缓存就用缓存,没有缓存就不发请求,当做请求出错处理(用于离线模式)

NSURLRequestReloadRevalidatingCacheData // 未实现

Http协议header字段,比如max-age,ETag,Last-Modified等字段的含义
  • UIWebView,WKWebView清理缓存的机制?
    WKWebView从iOS8推出,但是清理缓存的API要从iOS9开始

属性修饰符

  • block中修饰外面变量的修饰符? __block

  • 打破引用循环的关键字? weak;会自动设为nil,防止崩溃;如果能解释清楚自动设nil的原理,是高手

  • 默认的关键字?strong

  • nonatomic? 非线程安全,加快速度。

  • atomic? 对setter加锁,用@synchronized加锁,耗性能;对getter不加锁,不能做到“真正的线程安全”

  • assign、weak、unsafe_unretained的区别? assign用于基础类型,“如果用于对象,不会设为nil,会导致野指针,会带来崩溃”

  • copy?NSString,NSArray,NSDictionary,NSSet等。引用计数对这些集合类型无意义

block引用循环

  • 解决的方案weakSelf
 __weak __typeof(self)weakSelf = self;
[self.context performBlock:^{
     __strong __typeof(weakSelf)strongSelf = weakSelf; 
    [strongSelf doSomething];
    [strongSelf doMoreThing];
} ];
  • strongSelf的原因? 为了防止事情还没做完,weakSelf就变成了nil

  • block不需要用weakSelf的场合?
    动画的时候不需要,比如

self.alpha = 0;
[UIView animateWithDuration:0.2 animations:^ {
    self.alpha = 1;
}]

runtime动态特性

  • NSString *obj = [[NSSData alloc]init] ,obj在编译时和运行时分别是什么类型的对象?
    编译时是NSString, 运行时是NSSData的一个实例
  1. 字典转模型的原理?第三方库?
    YYModel
    属性列表,class_copyPropertyList

  2. 不埋点统计,怎么做到? 热更新JSPatch的原理?
    方法交换 Method Swizzling class_getInstanceMethod() method_exchangeImplementations()

  3. 如何给类别category添加属性?
    关联对象
    objc_setAssociatedObject objc_getAssociatedObject

  4. 如何在不创建类的前提下调用类的方法?用URL处理函数调用
    NSClassFromString()这个函数可以把URL中的字符串转化为本地定义的类
    performSelector方法执行相应的方法

Native和H5的交互方式

(1)截取URL,定义scheme,做相应处理
(2)JavaScriptCore框架jsContext,使用注入的方法
(3)WKWebView的注入方式

第三方库WebViewJavascriptBridge使用的是截取URL的方式,对UIWebViewWKWebView都适用

深复制,浅复制

NSArray *array = @[@"a", @"b", @"c", @"d"];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
  1. copyArrayarray的地址是否一样? 一样
  2. mCopyArrayarray的地址又是否一样? 不一样
  3. 改变mCopyArray里面的元素内容,是否会刻变array的内容? 不改变
  4. copymutableCopy的拷贝操作有何不同?
    copy浅拷贝,只是指针的复制,而内容未复制;
    mutableCopy是深拷贝,复制内容,新分配一段内存;

iOS架构

MVC
MVVM
MVP
VIPER

NSArray的实现方式?

指针的数组

需要区分流量来自哪个渠道,怎么做?

提升表格性能的方法?

(1)无用的网络请求及时取消
(2)表格cell重用
(3)提前算好cell的高度
(4)圆角不要用cornerRadius

Https和Http的区别?

Https == http + socket + TLS
iOS9开始,XCode默认用Https
如何继续用Http?在plist中将某个字段打开
企业版账号发布时,ipa包可以http的,但是那个plist文件是需要https的,不然不能自动安装程序

bitCode

从iOS9开始
好处是降低安装包的大小
苹果后台根据用户手机,只下传必要的图片

在ViewDidLoad中设置frame有什么问题?

UIImageView实现圆角的方法?

(1)Qurarz
(2)layerCornerRadius
(3)用path

连接蓝牙要注意些什么?

iOS中提供一个单例用于蓝牙相关的函数
(1)广播,用于扫描设备,选中特定设备
(2)建立点对点连接
(3)协议一般开头是兼容微信的头部,然后是自己的头部,然后是数据
(4)数据要按照一定的大小进行分包,每次传输的大小是受限的
(5)数据是NSData的,转化为有意义的自定义类型时要注意大端和小端字节序的问题
(6)传输时有奇偶校验的事情,要跟firmware端的约定好
(7)丢包重传,定时重连,空中升级等等都是比较令人头疼的内容

Object-C的一些容易误解的特性

  • 下面的代码输出什么?
@implementation Son : Father

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}

@end

// 输出
NSStringFromClass([self class]) = Son
NSStringFromClass([super class]) = Son

这个题目主要是考察关于Objective-C中对selfsuper的理解。我们都知道:self是类的隐藏参数,指向当前调用方法的这个类的实例。那super呢?
很多人会想当然的认为superself类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实super是一个 Magic Keyword,它本质是一个编译器标示符,和self 是指向的同一个消息接受者!他们两个的不同点在于:super会告诉编译器,调用class 这个方法时,要去父类的方法,而不是本类里的。
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前Son *xxx 这个对象。
当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super时,则从父类的方法列表中开始找。然后调用父类的这个方法。

你可能感兴趣的:(iOS面试建议)