定时器相关的问题
- 定时器的模式问题是被问的最多的
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)NSKeydeArchive
r归档存储, === 这个要实现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
的一个实例
字典转模型的原理?第三方库?
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
时,则从父类的方法列表中开始找。然后调用父类的这个方法。