定时器相关的问题
- 定时器的模式问题是被问的最多的
UIScrollView(UITableView) 拖动时执行的是 UITrackingRunLoopMode,会导致暂停定时器,等恢复为 NSDefaultRunLoopMode 时才恢复定时器。
所以如果需要定时器在UIScrollView 拖动时也不影响的话,建议添加到UITrackingRunLoopMode或 NSRunLoopCommonModes 中:
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串行队列,循环锁NSRecursiveLock,pthread的mutex,信号量等等
本地存储,本地缓存
沙盒的目录结构,使用场合
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的原理,是高手默认的关键字?
strongnonatomic? 非线程安全,加快速度。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就变成了nilblock不需要用weakSelf的场合?
动画的时候不需要,比如
self.alpha = 0;
[UIView animateWithDuration:0.2 animations:^ {
self.alpha = 1;
}]
runtime动态特性
-
NSString *obj = [[NSSData alloc]init] ,obj在编译时和运行时分别是什么类型的对象?
编译时是NSString, 运行时是NSSData的一个实例
字典转模型的原理?第三方库?
YYModel
属性列表,class_copyPropertyList不埋点统计,怎么做到? 热更新
JSPatch的原理?
方法交换Method Swizzling class_getInstanceMethod() method_exchangeImplementations()如何给类别
category添加属性?
关联对象
objc_setAssociatedObject objc_getAssociatedObject如何在不创建类的前提下调用类的方法?用
URL处理函数调用
NSClassFromString()这个函数可以把URL中的字符串转化为本地定义的类
用performSelector方法执行相应的方法
Native和H5的交互方式
(1)截取URL,定义scheme,做相应处理
(2)JavaScriptCore框架jsContext,使用注入的方法
(3)WKWebView的注入方式
第三方库WebViewJavascriptBridge使用的是截取URL的方式,对UIWebView和WKWebView都适用
深复制,浅复制
NSArray *array = @[@"a", @"b", @"c", @"d"];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
-
copyArray与array的地址是否一样? 一样 -
mCopyArray和array的地址又是否一样? 不一样 - 改变
mCopyArray里面的元素内容,是否会刻变array的内容? 不改变 -
copy和mutableCopy的拷贝操作有何不同?
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)layer的CornerRadius
(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中对self和super的理解。我们都知道:self是类的隐藏参数,指向当前调用方法的这个类的实例。那super呢?
很多人会想当然的认为super和self类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实super是一个 Magic Keyword,它本质是一个编译器标示符,和self 是指向的同一个消息接受者!他们两个的不同点在于:super会告诉编译器,调用class 这个方法时,要去父类的方法,而不是本类里的。
上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前Son *xxx 这个对象。
当使用self调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用super时,则从父类的方法列表中开始找。然后调用父类的这个方法。