iOS面试题 一共分为
笔试题
和面试题
两部分
笔试题
一共分为10个 总共613题
面试题
一共400题
笔试题 一个10个系列 分别为
①(语法篇) 共147题 已更新
②(常识篇) 共72题 已更新
③(界面篇) 共83题 已更新
④(iOS篇) 共52题 已更新
⑤(操作篇) 共68题 已更新
⑥(数据结构篇) 共23题 已更新
⑦(多线程篇) 共60题 已更新
⑧(网络篇) 共22题 已更新
⑨(多媒体篇) 共59题 已更新
⑩(安全篇) 共27题 已更新
面试题
一共分为3个 总共400题
⑪(面试篇 1/3) 共127题 已更新
⑪(面试篇 2/3) 共137题 已更新
⑪(面试篇 3/3) 共136题 已更新
View
的生命周期
及相关函数
是什么?你在开发中是如何用的?参考答案:
1.首先判断控制器是否有视图,如果没有就调用loadView方法创建:通过storyboard或者代码
2.随后调用ViewDidLoad,可以进行下一步的初始化操作;只会被调用一次;
3.在视图显示之前调用viewWillAppear;该函数可以多次调用;
4.视图viewDidAppear
5.在视图显示之前调用ViewWillDisappear;该函数可以多次调用;
6.在布局变化前后,调用viewWill / DidLayoutSubviews处理相关信息;
NSRunLoop
的实现机制
,及在多线程
中如何使用?参考答案:
NSRunLoop 是 iOS消息机制的处理模式 。
1. NSRunLoop 的 主要作用。:控制 NSRunLoop 里面线程的执行和休眠,在有事情做的时候使当前 NSRunLoop 的控制的线程工作,没有事情做让当前 NSRunLoop 的控制的线程休眠。
2. NSRunLoop 就是 一直循环检测,从线程 start 到线程 end ,检测 inputSource (如点击,双击等操作)异步事件,检测 timeSource 同步事件,检测到输入源会执行处理函数,首先会产生通知。 corefunction 向线程添加 RunLoop observers 来监听事件,意在监听事件发生时来做处理。
3. RunLoopmode 是一个集合, 包括监听:事件源,定时器,以及需要通知的 RunLoop observers 。
4.只有在为你的程序创建次线程的时候,才需要运行 RunLoop 。对于程序的主线程而言, RunLoop 是关键部分。Cocoa提供了运行主线程 RunLoop 的代码同时也会自动运行 RunLoop 。iOS程序 UIApplication 中的 run 方法在程序正常启动的时候就会启动 RunLoop 。如果你使用xcode提供的模板创建的程序,那你永远不需要自己去启动 RunLoop
5. 多线程中,你需要判断是否需要 RunLoop 。如果需要 RunLoop ,那么你需要负责配置 RunLoop 并启动。你不需要在任何情况下都去启动 RunLoop 。比如,你使用线程去处理一个预先定义好的耗时极长的任务时,你就可以无需启动 RunLoop 。 RunLoop 只在你要和线程有交互时才需要。
后台执行
内容有几种形式
,都是什么?参考答案:
一般的应用在进入后的时候可以获取一定时间来运行相关任务,也即是说可以在后台运行一小段事件(10S左右)。
1.后台播放音乐
2.后台GPS跟踪
3.后台voip支持
APP
的启动过程
,从main
文件开始?参考答案:
有 storyboard 情况下:
1.main函数
2. UIApplication
a.创建 UIApplication 对象
b.创建 UIApplication 的delegate对象
3.根据info.plist获得最主要 storyboard 的文件名,加载最主要的 storyboard (有 storyboard )
a.创建 UIWindow ;
b.创建和设置 UIWindow 的 rootViewController
c.显示窗口
没有 storyboard 的情况下:
1.main函数
2. UIApplication
a.创建 UIApplication 对象
b.创建 UIApplication 的delegate对象
3.delegate对象开始处理(监听)系统事件(没有 storyboard )
a.程序性启动完毕的时候,就会调用代理的 application:didFinishLaunchingWithOptions: 方法
b.在 application:didFinishLaunchingWithOptions: 中创建 UIWindow
c.创建和设置 UIWindow 的 rootViewController
d.显示窗口
程序
自己关掉
和程序进入后台
,远程推送
的区别?参考答案:
1.关掉后不执行任何代码,不能处理事件
2.应用程序进入后台状态不久后转入挂起状态。
在这种状态下,应用程序不执行任何代码,并有可能在任意时候从内存中删除。只有当用户再次运行此应用,应用才会从挂起状态唤醒,代码得以继续运行。
3.或者进入后台开启多任务状态,保留杂内存中,这样就可以执行系统运行的操作
4.远程推送是由远程服务器删的程序发送到APNS,再由APNS把消息推送到设备上的程序,当应用程序受到推送的消息会自动调用特定的方法执行事先写好的代码
本地通知
和远程推送
通知的基本概念
和用法
?参考答案:
本地通知和远程推送通知都可以向不在前台运行的应用发送消息,这种消息既可能即将发生的事件,也可能是服务器的新数据。不管是本地通知还是远程推送通知,它们在程序界面的显示效果相同,都可能显示为一段警告信息或应用程序图标上的徽章。
本地通知和远程推送通知的基本目的是让应用程序能够通知用户某些事情,而不需要应用程序在前台运行。
二者的区别在于本地通知由本应用负责调用,只能从当前设备上的iOS发出。
而远程通知由远程服务器上的程序发送到APNS,再由APNS把消息推送至设备上的程序
iOS开发库
和第三方库
?参考答案:
友盟(包含第三方登录和分享),高德地图,百度地图,AFN,SDWebImage,FMDB,MBProgressHUD等等
加载的比较慢
怎么处理?你是怎么优化程序
的性能
的?参考答案:
1.图片下载放在异步线程
2.图片下载过程中使用占位图片
3.如果图片较大,可以考虑多线程断点下载
实现
过一个框架
或库
以供别人使用么?如果有,请谈一谈构建经验;如果没有,请设想和设计框架
的API,并指出大需要如何做
、需要注意
一些什么方面?参考答案:
1.提供给外界的接口是否实用、够用
2.别人使用我的框架时,能不能根据类名、方法名就猜出接口的具体作用
3.别人调用接口时,提供的参数是否够用、调用起来是否简单
4.别人使用我的框架时,要不要导入依赖其他的框架
App
需要加载超大量的数据
,给服务器发送发送请求,但是服务器卡住了如何解决?参考答案:
1.设置请求超时
2.给用户提示请求超时
3.根据用户操作再次请求数据
实际开发
中,有哪些手机架构
与性能调试经验
?参考答案:
刚接手公司的旧项目时,模块特别多,而且几乎所有的代码都写在控制器里面,
比如UI控件代码、网络请求代码、数据存储代码
接下来采取MVC模式进行封装、重构
自定义UI控件封装内部的业务逻辑
封装网络请求工具类
封装数据存储工具类
即时通讯
中的大数据处理
?参考答案:
用put上传到文件服务器,然后发带url的自定义格式的给对方,对方接收到之后下载
网络数据
处理过程中,发现一处比较卡
,一般怎么解决
?参考答案:
1.检查网络请求操作是否被放在主线程了
2.看看异步请求的数量是否太多(子线程数量)
3.数据量是否太大?如果太大,先清除一些不必要的对象(看不见的数据、图片)
4.手机CPU使用率和内存问题
sqlite
锁定
的问题?参考答案:
1.设置数据库锁定的处理函数
int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
2.设定锁定时的等待事件
int sqlite3_busy_timeout(sqlite3*,60);
runtime
怎么添加属性
、方法
等?参考答案:
给系统的类添加属性的时候,可以使用runtime动态添加属性、方法。
本质:动态添加属性,就是让某个属性与对象产生关联
runtime一般都是针对系统的类。
class_addlvar
class_addMethod
class_addProperty
class_addProtocol
class_replaceProperty
runtime
如何实现weak
属性?参考答案:
weak策略表明该属性定义了一种非拥有关系(nonowning relationship) 。
为这种属性设置新值时,设置方法既不保留新值,也不是放旧值。此特质同assign类似;然而在属性所指的对象遭到摧毁时,属性值也会清空(nil.out).
runtime对注册的类,会进行布局,会将weak对象放入一个hash表中。用weak指向的对象内存地址
作为key。当此对象的引用计数为0的时候会调用对象的dealloc方法。假设weak指向的对象内存地址
是a。那么就会以a作为key,在这个weak的hash表中搜索,找到所有以a为key的weak对象,从而设
置为nil。
在ARC环境无论是强指针还是弱指针都无需在dealloc设置为nil,ARC会自动帮我们处理 即便是编译器不帮我们做这些,weak也不需要在dealloc中置nil。
模拟下weak的setter方法。大致如下-(void)SetObject:(NSObject *)object{ objc_setAssociatedObject(self,"object",object,OBJC_ASSOCIAtION_ASSIGN); [object cyl_runAtDealloc:^{ _object = nil; }]; }
runtime
如何通过selector
找到对应的IMP
地址?(分别考虑类方法和实例方法)参考答案:
每一个类对象都有一个对象方法列表(对象方法缓存) 。
类方法列表是存放在类对象中isa指针指向的原类对象中(类方法缓存)。
方法列表中每一个方法结构体中记录着方法的名称,方法实现,以及参数类型,
其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现。
当我们发送一个消息给一个NSObject对象时
这条消息会在对象的类对象方法列表里面查找。
当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找
runtime
Associate
方法关联的对象
,需要在主对象dealloc的时候释放
么?参考答案:
无论是在MRC下还是ARC下均不需要。
被关联的对象在生命周期内要比对象本身释放的晚很多,
它们会在被NSObject-dealloc调用的object_dispose()方法中释放。
对象
的内存销毁
时间表
,分哪几个步骤
?参考答案:
1.调用-release:因工银计数变为零;
对象正在被销毁,生命周期即将结束;
不能再有新的的__weak弱引用,否则将指向nil;
调用[self dealloc];
2.父类调用 -dealloc:继承关系中最直接继承的父类再调用 -dealloc;
如果是MRC代码则会手动释放实际变量们(iVars;继承关系中每一层的父类 都在调用 -dealloc)
3.NSObject 调用 -dealloc : 调用 Objective-C runtime中objec_dispose()方法
4.调用 objec_dispose():为c++的实例变量们(iVars)调用destructors:为ARC状态下的实例变量们(iVars)调用 -release;解除所有使用 runtime Associate方法关联的对象
解除所有 __weak引用;
调用free()
_objc_msgForward
函数是做什么的?直接调用它
将会发送什么
?参考答案:
_objc_msgForward是IMP类型。用于消息转发的。
当向一个对象发送一条消息,但它并没有实现的时候。
_objc_msgForward会尝试做消息转发。
直接调用 _objc_msgForward 是非常危险的事,这是把双刃刀,如果用不好会直接导致程序Crash
但是如果用得好,能做很多非常酷的事。
编译后
得到的类
找那个增加
实例变量
?能否向运行时
创建的类
中添加实例变量
?为什么?参考答案:
1.不能向编译后得到的类中增加实例变量;
2.能向运行时创建的类中添加实例变量;
分析如下:
因为编译后的类已经注册在runtime中,类结构体重的objc_ivar_list实例变量的链表和instance实例变量的内存大小已经确定,
同时runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak引用,
所以不能向存在的类添加实例变量
运行时创建的类是可以添加实例变量,
调用class_addIvar函数,
但是得在调用objc_allocateClassPair之后,
objc_registerClassPair之前,原因同上。
Objective-C
调用方法的过程
(runtime
)。参考答案:
Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即
objc_msgSend(recever,selector),整个过程介绍如下:
objc在向一个对象发送消息时,runtime库会根据对象isa指针找到该对象实际所属的类。
然后在该类中的方法列表以及其父类方法列表中寻找方法运行。
如果在最顶层的父类(一般也就是NSObject)中依然找到相应的方法时,
程序在运行时会挂掉并抛出异常 unrecognized selector send to XXX。
但是在这之前,objc的运行会给出三次拯救程序崩溃的机会。
补充说明:runtime铸造了Objective-C是动态语言的特性,使得C语言具有了面向对象的特性,在程序运行期创建,检查,修改类,对象及其对应的方法,这些操作都可以使用runtime的对应方法实现。
method swizzling
?参考答案:
简单说就是进行方法交换
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。
利用Objective-C的动态特性,可以实现运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放在方法的名字和方法实现的映射关系,selector的本质其实就是方法名,IMP优点类似函数指针,指向具体的Method实现,通过selector就可以找到对应的IMP。
交换方法的几种实现方式:
利用 method_exchangeImplementations 交换两个方法的实现
利用 class_replaceMethod 替换方法的实现
利用 method_setImplementation 来直接设置 某个方法的IMP
load
和initialize
的区别?参考答案:
initialize和load的区别在于:
load是只要类所在文件被引用就会被调用,
而initialize是在类或者其子类的第一个方法被调用前调用。
所以如果类没有比引用进项目,就不会有load调用。
但即使类文件被引用进来,但是没有使用到。那么initialize也不会被调用。
它们的相同点在于:
方法只会被调用一次。
方法调用的顺序:父类(SupeClass)的方法优先于子类(SubClass)的方法。
类中的方法优先于类别(Category)中的方法。
数组中
的元素去重复
: NSArray *array = @[@“12-11”,@“12-11”,@“12-11”,@“12-12”,@“12-13”,@“12-14”];参考答案:
MSMutableArray *resultArray = [[MSMutableArray alloc]initWithCapacity:array.count]; // 外层一个循环 for (NSString *item in array) { // 调用 - containsObject :本质也是要循环去判断。因此本质上是双层遍历 // 时间复杂度为O(n^2) 而不是O(n) if(![resultArray containsObject:item]){ [resultArray addObject:item]; } } NSLog("resultArray %@",resultArray);
集合
对数组中的元素去重复
:NSArray *array = @[@“12-11”,@“12-11”,@“12-11”,@“12-12”,@“12-13”,@“12-14”];参考答案:
集合操作可以通过valueForKeyPath里实现,去重可以一行代码实现:
array = [array valueForKeyPath:@"@distinctUnionOfObjects.self"]; NSLog("array %@",array);
但是返回的结果是无序的,与原来的顺序不同。
字典
对数组中的元素去重复
:NSArray *array = @[@“12-11”,@“12-11”,@“12-11”,@“12-12”,@“12-13”,@“12-14”];参考答案:
利用NSDictionary去重,字典在设置key-value时,若已经存在则更新值,若不存在则插入值,然后获取allValues。
若不要求有序,则可以采用此方法。若要求有序,还得进行排序。
效率分析:只需要一个循环就可以完成放入字典
若不要求有序,事件复杂度为O(n)。
若要求排序,则效率与排序算法有关。NSMutableDictionary *resultDict = [[NSMutableDictionary alloc]initWithCapacity:array.count]; for (NSString *item in array{ [resultDict setObject:item forKey:item]; } NSAarray *resultArray =resultDict.allValues; NSLog("resultArray %@",resultArray);
但是返回的结果是无序的,与原来的顺序不同。
NSSet
对数组中的元素去重复
:NSArray *array = @[@“12-11”,@“12-11”,@“12-11”,@“12-12”,@“12-13”,@“12-14”];参考答案:
利用集合NSSet的特性(确定性、无序性、互异性),放入集合就能自动去重了。
但是它和字典拥有同样的无序性,所得结果顺便不在于原来一样。NSSet *set = [[NSSet setWithArray:array]; NSArray *resultArray = [set allObjects]; NSLog("resultArray %@",resultArray); // 如果要求有序,那就得排序,比如这里要升序排序 resultArray = [resultArray sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1,id _Nonnull obj2){ NSString *item1 = obj1; NSString *item2 = obj2; return [item1 compare:item2 options:NSLiteralSearch]; }];
NSOrderedSet
对数组中的元素去重复
:NSArray *array = @[@“12-11”,@“12-11”,@“12-11”,@“12-12”,@“12-13”,@“12-14”];参考答案:
NSOrderedSet *set = [[NSOrderedSet orderedSetWithArray:array]; NSLog("NSOrderedSet %@",set.array);
NSArray
和NSMutableArray
的理解。参考答案:
NSArray 表示不可变数组,是有序元素集,只能存储对象类型,可以通过索引值直接访问元素,而且元素类型可以不一样。但是不能进行增、删、改操作。
NSMutableArray 是可变数组,能进行增、删、改操作。通过索引查询值很快,但是插入、删除等效率很多。
数组用于处理一组有序的数据集,比如常用的列表的DataSource要求有序,可以通过索引直接访问,效率高。
NSSet
和NSMutableSet
的理解。参考答案:
NSSet 表示不可变集合,具有确定性,互异性,无序性的特点。只能访问而不能修改集合。
NSMutableSet 是可变集合,可以对集合进行增、删、改操作。集合通过查询很快,插入、删除操作极快。
NSDictionary
和NSMutableDictionary
的理解。参考答案:
NSDictionary 表示不可变字典,具有无序性的特点,每个key对应的值是唯一的,可通过key直接获取值。
NSMutableDictionary 是可变字典,能对字典进行增、删、改操作。通过key查询值、插入、删除值都很快。
XIB
和Storyboards
,说一下他们的优缺点
。参考答案:
优点:
XIB:
在编译前就提供了可视化界面,可以直接拖控件,也可以直接给控件添加约束,更直观一些。
而且类文件中就少了创建控件的代码,确实简化不少,通常每个XIB对应一个类。
Storyboard:
在编译器提供了可视化界面,可拖控件,可加约束,在开发时比较直观,而且一个storyboard可以有很多的界面,每个界面对应一个类文件,通过storyboard,可以直观地看出整个App的结构。
缺点:
XIB:
需求变动时,需要修改XIB很大。有时候甚至重新添加约束,导致开发周期变长。
XIB载入相比纯代码自然要慢一下。对比复杂逻辑控制不同状态下显示不同内容时,使用XIB是比较困难的。
当多人团队或者多团队开发时。如果XIB文件被发动,极易导致冲突,而且解决冲突相对要困难多。
storyboard:
需要变动时,需要修改storyboard上对应的界面的约束,与XIB一样可能要重新添加约束,或者添加约束会造成大量的冲突。尤其是团队开发。
对于复杂逻辑控制不同显示内容时,比较困难。
当多人团队或多团队开发时。大家会同时修改一个storyboard,导致大量冲突,解决起来相当困难。
字符串
2018-03-11格式化日期
转为NSDate
类型参考答案:
NSString *timeStr = @"2018-03-11"; NSDateFormatter *formatter = [[NSDateFormatter alloc]init]; formatter.dataFormat = @"yyyy-MM-dd"; formatter.timeZone = [NSTimeZone defaultTimeZone]; NSDate *date = [formatter dateFromString:timeStr];
App
中混合HTML5开发
App如何实现
的。在App
中使用HTML5
的优缺点
是什么?参考答案:
在iOS中,通常是用UIWebView来实现的,当然iOS8以后可以使用WKWebView来实现,有以下几种实现方法 :
通过实现UIWebVIew的代理方法来拦截,判断scheme是否是约定好的。然后调用iOS本地相关API来实现。-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
在iOS7以后,可以直接通过JavaScriptCore这个库来实现,通过往JS DOM注入对象,而这个对象对应于我们iOS的某个类的实例。iOS加入H5响应比原生要慢很多,体验不太好,这是缺点。
iOS加入H5可以实现嵌入别的功能入口。可随时更改。不用更新版本就可以上线,这是最大的优点。
同步
和异步
,说说它们之间的的区别
参考答案:
首先,我们要明确一点,同步和异步都是在线程 中使用的。
在iOS开发中,比如网络请求数据时,
若使用同步请求 ,则只有请求成功或请求失败得到响应返回后,才能继续往下走,也就是才能访问其他的资源(会阻塞了线程)。
网络请求数据异步请求 时,不会阻塞线程,在调用请求后,可以继续往下执行,而不用等待请求有结果才能继续。
区别:
线程同步 :是多个线程访问同一资源时,只有当前正在访问的线程访问结束之后,其他线程才能进行访问(被阻塞)。
线程异步 :是多个线程在访问竞争资源时,可以在空闲等待时去访问其他资源(不被阻塞)。
内存管理
,在开发中对于内存的使用
和优化
包含哪些方面。我们在开发中应该注意哪些问题。参考答案:
对于内存的使用和优化有以下方面:
1.重用问题:如UITableviewCells、UICollectionViewCells,UITableviewHeaderForViews设置正确的reuseIdentifier,充分重用。
尽量把views设置不透明,当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提供性能。
2.不要使用太复杂的XIB/Storyboard:
载入时会将XIB/Storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才使用。哪些相比纯代码写的延迟加载,性能及内存就差了很多。
3.选择正确的数据结构:
学会选择对业务场景最合适的数组结构是写出高效代码的基础。
比如,
数组:有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。
字典:存储键值对,用键来查找比较快。
集合:无序的一组值,用值来查找很快,插入/删除很快。
4.gzip/zip压缩:
当从服务端下载相关附件时,可以通过gzip/zip压缩后在下载,使得内存更小,下载速度也更快。
5.延迟加载:
对于不应该使用的数据,使用延迟加载方式。
对于不需要马上显示的视图,使用延迟加载方式。
比如,网络请求失败时显示的提示页面。可能一直都不会使用到,因此应该使用延迟加载。
6.数据缓存:
对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于哪些网络数据,不需要每次都请求的。应该缓存起来,可以写入数据库,也可以通过plist文件存储。
7.重用大开销对象:
一些objects的初始化很慢。
比如 NSDateFormatter和NSCalendar,但又不可避免地需要使用它们。通常作为属性存储起来,防止反复创建。
8.避免反复处理数据:
许多应用需要从服务器加载功能所需要的常为JSON或者XML格式的数据。
在服务器端和客户端使用相同的数据结构很重要。
9.使用autoreleasePool:
在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存。
plist
文件时用来做什么
的。一般用它来处理
一些什么方面
的问题。参考答案:
plist是iOS系统中特有的文件格式。
我们常用的 NSUserDefault偏好设置实质上就是plist文件操作。
plist文件时用来持久化存储数据的。
我们通常使用它来存储偏好设置,以及哪些少量的,数据结构比较复杂的不适合存储数据库的数据。
比如
我们要存储全国城市名称和id,那么我们要优先选择plist直接持久化存储。因为更简单。
缓存
一定量的数据
以便下次可以快速执行
,那么数据
会存储
在什么地方,有多少种存储方式
?参考答案:
偏好设置(NSUserDefault)
plist文件存储
归档
Sqlite3
Core Data
苹果审核
时,遇到哪些问题
被拒绝。对于被拒绝的问题是如何处理
的?参考答案:
app中有虚拟物品交易。但是没有走内购导致被拒。
音频类App或者使用到音频相关的app,因为版权问题而被拒
App出现必闪退而被拒
Runtime
是什么?参考答案:
Objective-C的Runtime是一个运行时库(Runtime Library),它是一个主要使用C和汇编写的库,为C添加了面向对象的能力并创造了Objective-C
这就是说它在类信息(Class Information)中被加载,完成所有的方法分发,方法转发,等等
Objective-C runtime创建了所有需要的结构体,让Objective-C的面向对象编程变为可能。
Method Swizzling
原理参考答案:
在Objective-C中调用了一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。
利用Objective-C的动态性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。
IMP有点类似函数指针,指向具体的Method实现。
我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP。
我们可以利用 class_replaceMethod来修改类
我们可以利用 method_setImplementation 来直接设置某个方法的IMP
归根结底,都是偷换了selector的IMP。
深浅复制
和属性为copy
,strong
值得变化问题。参考答案:
浅复制:只复制指向对象的指针,而不复制引用对象本身。
对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是一个指针,对象本身资源还是只有一份,那如果我们对A_copy执行了修改操作。那么发现A引用的对象同样被修改了。
深复制就好理解了,内存中存在了两份独立对象本身[immutaableObject copy]; //浅拷贝 [immutaableObject mutableCopy]; //深拷贝 [mutaableObject copy]; //深拷贝 [mutaableObject mutableCopy]; //深拷贝
属性设置为copy,指定此属性的值不可更改,防止可变字符串更改自身的值得时候不会影响到对象属性(如 NSSting,NSArray,NSDictionary)的值。
strong此属性的值会随着变化而变化。
copy是内容拷贝。strong是指针拷贝。
NSTimer
创建后,会在哪个线程
运行。参考答案:
用scheduledTImerWithTimeInterval创建的,在哪个线程创建就会被加入到哪个线程的RunLoop中就运行在哪个线程。
自己创建的Timer,加入到哪个线程的RunLoop中就运行在哪个线程。
KVO
,NSNotification
,delegate
及block
区别是什么?参考答案:
KVO就是cocoa框架实现的观察者模式,一般同KVC搭配使用,通过KVO可以监听一个值得变化。比如View的高度变化。是一对多关系。一个值得变化会通知所有的观察者。
NSNotification是通知,也是一对多的使用场景,在某些情况下,KVO和NSNotification是一样的。都是状态变化之后告知对方。
NSNotification的特点,就是需要被观察者新主动发出通知,然后观察者注册监听后再进来响应。比KVO多了发送通知的异步,但是其优点是监听不局限属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。
delegate是代理,就是我不想做的事情交给别人做。比如够需要吃饭。就通过delegate通知主人,主人就会给他做饭,盛饭,倒水,这些操作,这些狗都不需要关系,只需要通过delegate(代理人)就可以了,有其他类完成所需要的操作。所以delegate是一对一关系。
block是delegate的另一种方式,是函数式编程的一种形式。使用场景和delegate一样,相比delegate更灵活。而且代理的实现更直观。
delegate一般的使用场景是行为,需要是需要别人帮我做一件事情,比如买卖股票,我们一般使用delegate。notification一般是进行全局通知。delegate是强关联。就是委托和代理双方相互直到。
Notification是弱关联,利好消息发出,你不需要知道是谁法的也可以做出相应的反应,同理发消息的人也不需要知道接收的人也可以正常发出消息。
计时器
调用一个类方法
?参考答案:
计时器只能调用实例方法,但是可以在这个实例方法里面调用静态方法。
使用计时器需要注意,计时器一定要加入RunLoop中,并且选好model才能运行。
scheduledTimerWithTimerInterval 方法创建一个计时器并加入到RunLoop中 所以可以直接使用
如果计时器的repeats 选择YES 说明这个计时器会重复执行,一定要在何时的时机调用计时器的invalid。不能在dealloc中调用,因为一旦设置 repeats为yes,计时器会强持self,导致dealloc永远不会被调用,这个类就永远无法被释放。
比如可以在ViewDidDisappear中调用,这样当类需要被回收的时候就可以正常进入dealloc中了。
类
的静态方法
需不需要release?参考答案:
静态方法,就是类方法
不需要,类方法对象放在autorelease中。
static
作用
?参考答案:
1. 函数体内static 变量的作用范围为该函数体,不同auto变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值
2. 在模块内的static 全局变量可以被模块内所有函数访问,但不能被模块外其它函数访问;
3. 在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明 它模块内
4. 在类中 static 成员变量属于整个类所拥有,对类的对象只有一份拷贝;
5. 在类中 static 成员变量属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量
递归方式
,反转二叉树
。参考答案:
public class TreeNode{ int val;ThreeNode left; TreeNode right; TreeNode(int x) {val = x;} } public class Solution { public TreeNode invertTree(TreeNode root){ if(root == null){return null}; root.left = invertTree(root.left); root.right = invertTree(root.right); TreeNode tmp = root.left; root.left = root.right; root.right = root.tem; return root; } }
不要
使用递归方式
,反转二叉树
。参考答案:
+(BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode*)rootNode{ if(!rootNode){return nil;} if(!rootNode.leftNode && !rootNode.rightNode) {return rootNode;} NSMutableArray *queueArray = [NSMutableArray array]; //数组当成队列。 [queueArray addObject:rootNode];// 压入根节点 while(queueArray.count > 0 ){ BinaryTreeNode *node = [queueArray firstObject]; [queueArray removeObjectAtIndex:0]; //弹出最前面的节点,仿照队列先进先出原则。 BinaryTreeNode *pLeft = node.leftNode; node.leftNode = node.rightNode; node,rightNode = PLeft; if(node.leftNode){[queueArray addObject:node.leftNode]} if(node.rightNode){[queueArray addObject:node.rightNode]} } return rootNode; }
objective-C
编写一个单例
。参考答案:
+(AccountManager *)sharedManager{ static AccountManager *sharedAccountManagerInstance = nil; tatic dispatch_once_t predicate; dispatch_once(&predicate,^{ sharedAccountManagerInstance = [[self alloc]init]; }); return sharedAccountManagerInstance; }
Swift
编写一个单例
。参考答案:
final class SingleClass : NSObject{ static shared = SingleClass() private override init(){} func say(){ print("Hello,LYH"); } }
应用程序
的状态
有哪些?参考答案:
1.Not running 未运行:程序没启动。
2.inactive 未激活:程序在前台运行,不过没有接收到事件。在没有事件处理情况下程序通常停留在这状态
3.Active激活:程序在前台运行而且接收到了事件。这也是前台的一个正常的模式。
4.Background: 程序在后台而且能执行代码,大多数程序进入这个状态后在这个状态上停留一会。时间到之后进入挂起状态(Suspended)。有的程序经过特殊的请求后可以长期处于background状态。
5.Suspended挂起:程序在后台不能执行代码。系统会自动把程序编程这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就会把挂起的程序清理掉,为前台程序提供更多的内存。
post
和get
的区别?参考答案:
1. GET使用URL或Cookie传参。
而POST将数据放在BODY中。
2.GET的URL会有长度上的限制,则POST的数据则可以非常大。
3.POST比GET安全,因为数据在地址栏上不可见。
事件响应链
Responder Chain?参考答案:
响应者对象 (Responder Object),指的是由响应和处理事件能力的对象。响应者链就是由一系列的响应者对象构成的一个层次结构。
UIResponder 是所有响应对象的基类,在UIResponder类中定义了处理上述各种事件的接口。
我们属性的UIApplication、UIViewController、UIWindow和所有继承自UIView的UIKit类都直接或间接的继承UIResponder,所有它们的实例都是可以构成响应者链的响应者对象。
响应者链有以下特点:
1.响应者链通常是由视图(UIView)构成的;
2.一个视图的下一个响应者是它视图控制器(UIViewController)(如果有的话),然后再转给它的父视图(Super View);
3.视图控制器(如果有的话)的下一个响应者为其管理的视图的父视图
4.单例的窗口(UIWindow)的内容视图将指向窗口本身作为它的下一个响应者。
5.单例的应用(UIApplication)是一个响应者链的终点,它的下一个响应者指向nil,以结束整个循环。
屏幕
是如何互动
的?参考答案:
iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动application的事件队列,单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,UIWindow对象首先会使用hitTest:withEvent:方法寻找此次的Touch操作初始点所在的视图(UIView),即需要将触摸事件传递给其处理的视图,这个过程称之为hit-test view。
UIWindow实例对象会首先在它的内容视图上调用hitTest:withEvent:,此方法会在其视图层次结构中的每个视图上调用pointInside:withEvent:(该方法用来判断点击事件发生的位置是否处于当前视图范围内,以确定用户是不是点击了当前视图),如果pointInside:withEvent:返回YES,则继续逐级调用,知道找到touch操作发送的位置,这个视图也就是要找的hit-test view。
事件的传递和响应分两个链:
传递链:由系统向离用户最近的view传递。
UIKit > active app‘s event queue > window > root View > lowest view
响应链: 由离用户最近的view 向系统传递。
inital view > super view > view Controller > window > Application
RunLoop
是什么,使用的目的
,何时使用
和关注点
?参考答案:
RunLoop是一让线程能随时处理事件但不退出的机制。
RunLoop实际上是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行Event Loop的逻辑。
线程执行了这个函数后,就会一直处于这个函数内部“接受消息->等待->处理”的循环中,直到这个循环结束(比如传入quit的消息),函数返回。
让线程在没有处理消息时休眠以避免资源占用、在有消息到来立刻被唤醒。
OSX/iOS系统中,提供了两个这样的对象:
NSRunLoop和CFRunLoopRef。
CFRunLoopRef是在CoreFoundation框架内的,它提供了纯C函数的API,所有这些API都是线程安全的。
NSRunLoop是基于CFRunLoopRef的封装,提供了面向对象的API。但是这些API不是线程安全的。
线程和RunLoop之间是一一对应的。其关系是保存在一个全局的Dictionary里。
线程刚创建时并没有RunLoop,如果你不主动获取,那它一直都不会有。
RunLoop的创建时发送在第一次获取时,RunLoop的销毁是发生线程结束时。
你只能在一个线程的内部获取其RunLoop(主线程除外)。
RunLoop
有哪些作用
?参考答案:
使程序一直运行接受用户输入
决定程序在何时应该处理哪些Event
调用解耦
节省CPU时间
使用
RunLoop
?参考答案:
仅当在为你的程序创建辅助线程的时候,你才需要显式运行一个RunLoop。
RunLoop是程序主线程基础设施的关键部分。
所有,cocoa和Carbon程序提供了代码运行主程序的循环并自动启动RunLoop。
iOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)作为程序启动步骤的一部分,它在程序正常启动的时候就启动程序的主循环。
类似地,RunApplicationEventLoop函数为Carbon程序启动主循环。
如果你使用Xcode提供的模板创建你的创新,那你永远不需要自己去显式的调用这些例程。
对于辅助线程,你需要判断一个RunLoop是否是必须的。
如果是必须的,那么你要自己配置并启动它。
你不需要在任何情况下都去启动一个线程RunLoop。
比如,你使用线程来处理一个预先定义的长时间运行的任务时,你应该避免启动RunLoop。
RunLoop在你要和线程有更多的交互才需要。比如以下情况:
使用端口或自定义输入源来和其他线程通信
使用线程的定时器
cocoa中使用任何performSelector…的方法
是线程周期性工作
多线程处理方式
及优缺点
是什么?参考答案:
iOS有四种多线程编程的技术,分别是
NSThread、Cocoa NSOPeration、Cocoa GCD和 pthread。
1.NSThread
优点:NSThread比其他两个轻量级。
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据地加锁会有一定的系统开销
2.Cocoa NSOPeration
优点:不需要关心线程管理,数据同步的事情。可以把精力放在自己需要执行的操作上 。 Cocoa NSOPeration相关的类是 NSOPeration,NSOPerationQueue。
NSOPeration是一个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类:NSInvocationOperation和NSBlockOperation。创建NSOPeration子类的对象。把对象添加到NSOPerationQueue队列里执行。
3.GCD(全优点) Grand Central dispatch (GCD)是Apple开发的一个多核编程的解决方案。在iOS4.0开始之后才能使用。GCD是替代NSThread,NSOPerationQueue,NSInvocationOperation等技术的很高效强大的技术。
pthread是一套通用的多线程API,适用于Linux\Windows\Unix,跨平台,可移植,使用C语言,声明周期需要程序员管理,iOS开发中使用很少。
dispatch_sync
和dispatch_async
区别是什么?参考答案:
dispatch_asyn(queue,block)async 异步队列,dispatch_asyn函数会立即返回,block会在后台异步执行。
dispatch_sync(queue,block)sync 同步队列,dispatch_sync函数不会立即返回,及阻塞当前线程,等待block同步执行完成。
__weak
和__block
参考答案:
__weak和weak基本相同,前者用于修饰变量(variable),后者用于修饰属性(property)。
__weak主要是防止block中的循环引用
__block也是用于修饰变量,它是引用修饰,所以其修饰的值是动态变化的,即可以被重新复制的。
__block用于修饰某些block内部将要修改的外部变量。
__weak和__block的使用场景几乎与block息息相关。
而所谓的block,就是Objective-C对闭包的实现。
闭包就是没有名字的函数,或者理解为指向函数的指针。
ARC
?参考答案:
ARC 全称是 Automatic Reference Counting
,是Objective-C的内存管理机制。
简单地来说,就是代码中自动加入了retain/release ,原先需要手动添加的 用来处理内存管理的引用计数的代码 可以自动地由编译器完成了。
ARC 的还是用是为了解决对象的retain 和release 匹配的问题。
以前是手动管理造成内存泄露或者重复释放的问题将不复存在。
以前需要手动的通过retain 去为对象获取内存,并用release 释放内存。
所有以前的操作称为MRC(Manual Reference Counting) 。
scheduledTImerWithTimeInterval
的方式触发的timer
,在滑动页面
上的列表时,timer
会暂停
,为什么?如何解决?参考答案:
原因是在于滑动时当前线程的runLoop 切换了mode用于列表滑动,导致timer暂停。
runLoop的mode主要用来指定事件在runLoop中的优先级,有以下几种:
Default(NSDefaultRunLoopModel):默认,一般情况下使用;
Connecton(NSConnectionReplyMode):一般系统用来处理NSConnection相关事件,开发者一般用不到
Modal(NSModalPanelRunLoopMode):处理modal panels事件;
Event Tracking(NSEventTrackingRunLoopMode):用于处理拖拽和用户交互的模式。
Common(NSRunLoopCommonModes):模式集合。默认包含Default、Modal、Event、Tracking三大模式,可以处理几乎所有的事件。
回到题中的情境。滑动列表时,runLoop的mode由原来的Default模式切换到了Event Tracking模式,timer原来好好工作的运行在Default模式中,被关闭后自然停止工作了。
解决方法是其一将timer加入到NSRunLoopCommonModes中。
其二是将timer放到另一个线程中,然后开启另外一个线程runLoop,这样可以保证与主线程互不干扰,而现在主线程正在处理页面滚动。
class
)和结构体(struct
)有什么区别?参考答案:
Swift中,类是引用类型,结构体是值类型
值类型在传递和赋值将进行复制,而引用类型则只会使用引用对象的一个“指向”
所以它们两者之间的区别就是两个类型的区别
class
有哪些功能
是struct
没有的?参考答案:
class可以继承 ,这样子类可以使用父类的特征和方法
类型转换可以在runtime的时候检查和解释一个实例的类型
可以用deinit来释放资源
一个类可以被多次引用
struct
相对于class
有什么优势?参考答案:
结构较小,适用于复制操作,相比于一个class的实例被多次引用更安全。
无须担心内存memory leak或者多线程冲突问题
面向对象
还是函数式
的编程语言
?参考答案:
Swift既是面向对象的,又是函数式的编程语言。
说Swift是面向对象语言,是因为Swift支持类的封装、继承、多态,从这点上来看与Java这类纯面向对象的语言几乎毫无差别。
说Swift是函数式编程语言,是因为Swift支持map,reduce,filter,flatmap这类去除中间状态、数学函数式的方法。更加强调运算结果而不是中间过程。
Open
,Public
,Internal
,File-private
,Private
?参考答案:
Swift有五个级别的访问控制权限,从高到低依次为
比如:Open,Public,Internal,File-private,Private
它们遵守的基本原则是:高级别的变量不允许被定义为低级别变量的成员变量。
比如 一个private的class中 不能包含 public 的 String。
反之,低级别的变量却可以定义在高级别的变量中。
比如 public的 class 可以含有 private的 Int。
Open具有最高的访问权限。其修饰的类和方法可以在任意Module中被访问和重写;它是Swift 3新添加的访问权限。
Public的权限仅次于Open。于Open唯一的区别在于它修饰的对象可以在任意Module中访问,但不能重写。
Internal是默认的权限。它表示只能在当前定义的Module中访问和重写。它可以被一个Module中的多个文件访问。但是不可以被其他的Module中被访问。
File-private也是Swift 3 新添加的权限。其被修饰的对象只能在当前文件中被使用。例如它可以被一个文件的class,extension,struct共同使用。
Private是最低的访问权限。它的对象只能在定义的作用域内使用。离开了这个作用域。即时是同一个文件中的其他作用域,也无法访问。
strong
,weak
,unowned
?参考答案:
Swift的内存管理机制与Objective-C一样为ARC(automatic Reference Counting)。
它的基本原理是,一个对象在没有任何强引用指向它时,其占用的内存会被回收。
反之,只要有任何一个强引用指向该对象,它就会一直存在内存中。
strong代表着强引用,是默认属性。
当一个对象被声明strong时,就表示父层级对该对象有一个强引用的指向。
此时该对象的引用计数会增加1.
weak代表着弱引用。
当对象被声明为weak时,父层级对此对象没有指向。该对象的引用计数不会增加1.
它在对象释放后弱引用也随即小时。
继续访问该对象,程序会得到nil。不会崩溃。
unowned与弱引用本质上一样。
唯一不同的是。对象在释放后,依然有一个无效的引用指向对象。
他不是Optional也不指向nil。
如果即系访问该对象,程序会崩溃。
weak
和unowned
的使用场景有哪些差别。参考答案:
当访问对象时该对象可能已经被释放了。则用weak。比如delegate的修饰。
当访问对象确定不可能被释放,则用unowned。比如self的引用。
实际上为了安全期间,很多公司规定任何时候都使用weak去修饰。
或(||)
操作参考答案:
func ||(left: Bool,right: Bool) -> Bool { if left{ return true } else{ return right } }
实现一个函数
。求一个整型二维数组
中所有元素之和
?参考答案:
func sumPairs(_ nums:[[int]]) -> Int { return nums.flatMap{$0}.reduce(0) {$0 + $1} }
Swift有函数式编程的思想。
其中flatMap函数,mapreduce,filter是其代表的方法。
本题中考察了flatMap的降维思路,以及reduce的基本使用。相比于一般的for循环。这样的写法更加的简洁漂亮。
String
,Array
,Dictionary
设计成值类型
?参考答案:
要解答这个问题,就和要Objective-C相同的数据结构设计进行比较。
Objective-C中,字符串,数组,字典,皆被设计为引用类型。
值类型相比引用类型,最大的优势在于内存使用的高效。
值类型在栈上操作,引用类型在堆上操作。
栈上的操作仅仅是单个指针的上下移动。
而堆上的操作则牵涉到合并、移位、重新连接等。
也就是说Swift这样设计,大幅减少了堆上的内存分配和回收的次数。
同时copy-on-write 又将值传递和复制的开销降到了最低。
String,Array,Dictionary设计成值类型,也是为了线程安全考虑。
通过Swift的let设置。使得这些数据达到了真正意义上的“不变”,它也从根本上解决了多线程中内存访问和操作顺序的问题。
设计成值类型还可以提升API的灵活度。例如通过实现Collection这样的协议。我们可以遍历String,使得整个开发更加灵活高效
Swift
和Objective-C
的混编项目
中,如何在Swift
文件中调用
Objective-C
文件已经定义的方法
?如何在Objective-C
文件中调用
Swift
文件中的方法
?参考答案:
Swift中若要使用Objective-C代码,可以在ProjectName-Bridging-Header.h添加Objective-C头文件名称,Swift文件即可调用相应的Objective-C代码。
一般情况Xcode会在Swift项目中第一次创建Objective-C文件时自动创建ProjectName-Bridging-Header.h文件。
Objective-C中若要调用Swift代码,可以导入Swift生成的头文件ProjectName-Swift.h来实现。
Swift文件中若要规定固定的方法或属性暴露给Objective-C使用,可以在方法或属性前加上@objc来声明。
如果该类是NSObject子类,那么Swift会在非private的方法或属性前自动加上@objc。
Swift
将协议
(protocol
)中的部分方法设计成可选
(optional
),怎么实现?参考答案:
@optional 和 @required是Objective-C中的特有关键字。
Swift中,默认所有方法在协议中都是必须实现的。而且,协议里方法不可以直接定义optional,先给出两个解决方案:
在协议和方法前面都加上 @objc关键字,然后再方法前面加上optional关键字。
该方法实际上是把协议转换为(Objective-C)的方式然后进行可选定义。
第2种方式用到扩展(extension)来规定可选方法。
Swift中,协议扩展(protocol extension)可以定义部分方法的默认实现,这样这些方法在实际调用中就是可选实现的了
UIButton
增加一个点击后抖动
的效果,该怎样实现?参考答案:
实现一个自定义的UIButton类,在其中添加抖动效果的方法(shake)方法。
写一个UIButton或者UIView的扩展(extension),然后再其中添加(shake)方法
定义了一个protocol 然后再协议扩展(protocol extension)中添加shake方法。
Swift
和Objective-C
中的初始化方法(init)
有什么异同
?参考答案:
Swift中的初始化方法更加严格和准确。
Objective-C中,初始化方法无法保证所有成员变量都初始化;编译器对属性设置并无警告,但是实际操作中会出现初始化不完全的问题;初始化方法与普通方法并无实际差别,可以多次调用。
Swift,初始化必须保证所有optional的成员变量都完成初始化。同时新增convenience和required 两个修饰初始化方法的关键词。
convenience 只是提供一个方便的初始化方法,必须通过调用同一个类designated初始化方法来完成。
required是强制子类重写父类中所修饰的初始化方法。
Objective-C
和Swift
动态特性
的理解?参考答案:
runtime其实就是Objective-C的动态机制。
runtime执行的是编译后的代码,这时它可以动态加载对象,添加方法、修改属性、传递信息等等。
具体过程是在Objective-C中对象调用方法时,
如[self.tableview reload].发送了两件事。
编译阶段。编译器(compiler)会把这句话翻译成objc_msgSend(self.tableivew,@selector(reload)),把消息发送给self.tableview.
运行阶段,接受者self.tableview会响应这个消息。期间可能会执行,转发消息,也可能会找不到方法崩溃。
所以整个流程是编译器翻译 -> 给接收者发送消息 -> 接受者响应消息三个流程。
Button
,在你手
触摸屏幕点击后
,到这个Button受到点击,中间发生了什么
?参考答案:
响应链大概有以下几个步骤:
设备将touch到UITouch和UIEvent对象打包,放到当前活动的application的事件队列中。
单例的application会从事件队列中取出触摸事件传递给单例UIWindow。
UIWindow使用hitTest:withEvent:方法查找touch操作的所在的视图view。
RunLoop这边是这样的:
主线程的RunLoop被唤醒通知Observer,处理Timer和Source 0 Springboard 接受 touch event 之后转给App进程
RunLoop 处理 Source 1,Source 1 就会触发回调,并调用_UIApplicationHandleEventQueue()进行应用内部的分发。
RunLoop处理完毕进入睡眠,此前会释放旧的 autorelease pool 并建立一个 autorelease pool
内存
的几大区域
?参考答案:
1.栈区(stack)由编译器自动分配并释放,存放函数的参数值,局部变量等。
栈是系统数据结构,对应线程/进程是唯一的。
优点是快速高效,
缺点是有限制,数据不灵活。
栈空间分静态分配和动态分配两种。
2.堆区(heap)由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收,
比如在iOS中alloc都是存放在堆中。
优点是灵活方便,数据适应面广泛,但是效率有一定降低。
虽然程序结束时所有的数据空间都会被释放会系统。
但是精准的申请内存,释放内存匹配是良好程序的基本要素。
3.全局区(静态区)(static)全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放;
4.文字常量区 存放常量字符串,程序结束后由系统释放;
5.代码区 存放函数的二进制代码
组件化
解耦
的?参考答案:
「组件化」顾名思义就是把一个大的App拆成一个个小的组件,相互之间不直接引用。
实现代码的高内聚低耦合,方便多人团队开发!
一般需要解耦的项目都会多多少少出现,以下几个情况:
1.耦合比较严重(因为没有明确的约束。「组件」间引用的现象会比较多)
2.容易出现冲突(尤其是使用Xib,还有就是XcodeProject 虽说有脚本可以改善)
3.业务方的开发效率不够高(只关心自己的组件,却要编译整个项目,与其他不相干的代码糅合在一起)
runtime
如何通过selector
找到对应的IMP
地址?参考答案:
类对象中有类方法和实例方法的列表,列表中记录这个方法的名称,参数和实现,而selector本质就是方法名称,runtime通过方法名称就可以在列表中找到该方法对应的实现。
这里声明了一个指向 struct objc_method_list指针的指针,可以包含类方法列表和实例方法列表
具体实现
在寻找IMP的地址时,runtime提供了两个方法
IMP class_getMethodImplementation(Class cls,SEL name);
IMP method_getMethodImplementation(Method m);
runtime
内部实现逻辑?参考答案:
实际上RunLoop就是这样一个函数。其内部是一个do-while循环。
当你调用CFRunLoopRun()时。线程就会一直停留在这个循环里;
直到超时或被手动停止,该函数才会返回。
GCD
执行原理
?参考答案:
GCD有一个底层线程池,这个池中存放的是一个个的线程。
之所以称为“池”,很容易理解出这个“池”中的线程是可以重用的,当一段时间后这个线程没有被调用,这个线程就会被销毁。
注意:开多少条线程是由底层线程池决定的,池是系统自动来维护。
而程序员只关心的是向队列中添加任务,队列调度即可。如果队列中存放的是同步任务,则任务出队后,底层线程池中会提供一条线程供这个任务执行,任务执行完毕后这条线程在回到线程池。
这样队列总的任务反复调度,因为是同步的,所以当我们用currenTread打印的时候,就是同一条线程。
如果队列存放的是异步的任务,当任务出队后,底层线程池会提供一个线程任务执行,因为是异步执行,队列中的任务不需要等待当前任务执行完毕就可以调度下一个任务,这时底层线程池会再次提供一个线程供第二个任务执行,执行完毕后再回到底层线程池中。
这样就对线程完成一个复用,而不需要每一个任务执行都开启新的线程,也就是从而节约的系统的开销,提高了效率。
防止
别人反编译
你的app
?参考答案:
1.本地数据加密:对UserDefaults,sqlite存储文件数据加密,保护账号和关键信息
2.URL编码加密:对程序中出现URL进行编码加密,防止URL被静态分析
3.网络传输数据加密:对客户端传输数据提供加密方案,有效防止通过网络接口的拦截获取数据
4.方法体,方法名高级混淆:对应用程序的方法名和方法体进行混淆,保证源码被逆向后无法解析代码
5.程序结构混排加密:对应用程序逻辑结构进行打乱混排,保证源码可读性降到最低。
借助第三方APP加固:网易云易盾
优化
应用程序
的启动速度
?参考答案:
1.启动过程中做的事情越少越好(尽可能将多个接口合并)
2.不在UI线程上做耗时操作(数据地处理在子线程进行,处理完成通知主线程刷新即可)
3.在何时的时机开启后台任务(例如在用户指引界面就可以开始准备加载的数据)
4.尽量减少包的大小
优化
应用程序
的页面
浏览速度
?参考答案:
1.JSON的处理(iOS自带的NSJSONSerialization,JSONKit,SBJson)
2.数据的分页(后端数据多的话,就要分页返回,例如网易新闻,或者微博记录)
3.数据压缩(大数据也可以压缩返回,减少流量,加快反应速度)
4.内容缓存(例如网易新闻的最新新闻列表都是要缓存到本地,从本地加载,可以缓存到内存,或者数据库,根据情况而定)
5.延迟加载tab(比如app有5个tab,可以先加载第一个要显示的tab,其他的显示时候加载,按需加载)
6.算法的优化(核心算法的优化,例如有些app有个联系人姓名用汉语拼音的首字母排序)
优化
应用程序
的操作流畅度
?参考答案:
1.tableview优化 (tableview cell的加载加载优化)
2.ViewController加载优化 (不同view之间的跳转,可以提前准备好数据)
优化
应用程序
的客户端
和服务端
的交互
?参考答案:
1.客户端尽量减少请求
2.服务端尽量做多的逻辑处理
3.服务器端和客户端采取推拉结合的方式 (可以利用一些同步机制)
4.通讯协议的优化 (减少报文的大小)
5.电量使用优化 (尽量不要使用后台运行)
优化
应用程序
的数据库
?参考答案:
1.数据库设计上面的重构
2.查询语句的优化
3.分库分表(数据太多的时候,可以分不同的表或者库)
tableview
卡顿
嘛会造成
卡顿
的原因
大致有哪些?参考答案:
可能造成tableview卡顿的原因有:
1.没有使用到cell的重用机制,注册重用标识符。
2.避免cell的重新布局。cell的布局填充等操作比较耗时,一般创建时就布局好。
3.提前计算并缓存cell的属性及内容,当我们创建cell的数据源方法时。编译器并不是先创建cell再定cell的高度。
4.减少cell中控件的数量,不同风格的cell可以使用不用的重用标识符,初始化添加控件,不适合的可以先隐藏。
5.不要使用ClearColor,无背景色。透明度也不要设置为0,渲染耗时比较长。
6.使用局部更新,如果只是更新某组的话,使用reloadSection进行局部更新。
7.加载网络数据,下载图片,使用异步加载,并缓存。
8.少使用addView给Cell动态添加view。
9.按需加载cell,cell滚动很快时,只加载范围的cell。
10.不要实现无用的代理方法,tableview只遵循两个协议。
11.预渲染图片。当新的图片出现,仍然会有短暂的停顿线性。解决的版本就是在bitmapcontext里面先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;
timer
准吗
?谈谈你的看法?如果不准
该怎么实现一个精准的Timer
?参考答案:
不准,原因
1.Timer加到main runloop中,模式是DefaultRunLoopMode,main负责所有主线程事件,例如UI界面的操作,复杂的运行,这样在同一个RunLoop中Timer就会产生阻塞。
2.模式的改变。主线程的RunLoop里有两个预置的Mode:KCFRunLoopDefaultMode和 UITrackingRunLoopMode。
当你创建一个Timer并加到DefaultMode时,Timer会得到重复回调,单此时滑动一个ScrollView时,RunLoop会将mode切换TrackingRunLoopMode,这时Timer就不会被回调,并且也不会影响到滑动操作。
所以就会影响Timer不准的情况。
实现一个精准的Timer。
方法一:在主线程中进行Timer操作,但是将Timer实例加到main runloop的特定mode(模式)中。避免被复杂运算操作或者UI界面刷新所干扰。
方法二:使用mach内核级的函数可以使用mach_absolute_time()获取到CPU的tickcount的计数值。可以通过“mach_timebase_info”函数获取纳秒级的精准度。然后使用mach_wait_until(uint64_t deadline)函数,直到指定的时间之后,就可以执行指定任务了
编译过程
做了
哪些事情
?参考答案:
1.C++,Objective-C都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高。
iOS开发目前的常用语言是:Objective和Swift。二者都是编译语言,换句话来说都是需要编译才能执行的。二者的编译都是依赖于Clang+LLVM。
iOS编译:不管是OC还是Swift,都是采用Clang作为编译器前端,LLVM(Low level vritual machine)作为编译后端。
编译器前端的任务时进行:语法分析,语义分析,生成中间代码(intermediate representation)。
在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。
编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。
LLVM机器码生成器会针对不同的架构。
比如arm64等生成不同的机器码。
编译各个.m文件,使用compileC和clang命令。
内存管理
的本质原因
?参考答案:
1.OC对象放在堆中
2.非OC对象存在栈中(栈内存会被系统自动回收)
isa
指针,怎么用,用来干嘛?参考答案:
每个Objective-C对象都有一个隐藏的数据结构。这个数据结构就是Objective-C对象的第一个成员变量,他就是isa指针,这个指针是指向对象的真实类型,根据这个指针就能直到将来调用哪个类的方法
每个实例对象都有个isa指针,他指向对象的类,而类对象里也有个isa指针。指向metaClass(元类)。元类保存了类方法的方法列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向它父亲查找该方法。
同时注意的是:元类(metaClass)也是类,他也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass),其实就是NSObject,根元类的isa指针指向本身,这样形成了一个封闭的内循环。
@synthesize
和@dynamic
分别有什么作用
?参考答案:
@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@synthesize var _var;
@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
@dynamic 告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成(当然对于readonly的属性只需要提供getter即可)
假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法。编译的时候没问题。
但是当程序运行到 instance.var = someVar,由于缺setter方法会导致程序崩溃。
或者当运行到 someVar = instance.var ,由于缺getter方法同样会导致崩溃。
编译时没问题,运行时才执行相应的方法。这就是所谓的动态绑定。
KVO
的内部实现
原理
?参考答案:
KVO是基于runtime机制实现的。当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类的任务被观察事件的setter方法。
派生类被重写的setter方法内实现真正的通知机制。
如果原类为Person,那么生成的派生类名为NSKVONotifying_Person每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时,执行的是派生类的setter方法。
键值观察通知依赖于NSObject的两个方法:
willChangeValueForKey:和didChangevalueForkey。
在一个被观察属性发生改变之前,willChangeValueForKey:一定会被调用。这就会记录旧的值。
而当改变发生后,didChangevalueForkey会被调用,继而observeValueForKey:ofObject:change:context:也会被调用。
Home键
进入后台时的生命周期
,以及从后台回到前台
时的生命周期
?参考答案:
App生命周期:
1.刚启动时:
application:didFinshLaunching:withOptions:
applicationDidBeconmeActive
2.进入后台时
applicationWillResignActive
applicationDidEnterBackground
3.进入前台时
applicationWillEnterForeground
applicationDidBeconmeActive
4.ViewController生命周期:
init/awakeFromNib > loadView > viewDidLoad > viewWillAppear > viewDidAppear:
NSOPerationQueue
相对于GCD来
说有哪些优点
?参考答案:
1.提供了在GCD中不那么容易复制的有用特性。
2.可以很方便的取消一个NSOPeration的执行
3.可以更容易的
4.提供了任务的状态:isExecuteing,isFinished
5.可以通过设置maxConcurrentOperationCount属性来控制并发任务的数量
runtime
如何实现weak
变量的自动置nil?参考答案:
runtime对注册的类,会进行布局。对于weak对象会放入一个hash表中。
用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候 会dealloc,加入weak指向的对象内存地址是a,那么就会以a为键,在这个weak表中搜索,找到所有以a为键的weak对象,从而设置nil。
常用设计模式
及应用场景
?参考答案:
1、代理模式
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合。
2.观察者模式
应用场景:一个对象状态改变,通知正在对他进行观察的对象,这些对象根据各种要求做出相应的改变。
优势:解耦合
3.单例模式
应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
4.工厂模式
应用场景:工厂方式创建类的实例,多于Proxy模式配合。创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
UIButton
的继承关系
?参考答案:
UIButton : UIControl : UIView : UIResponder : NSObject
UITableview
的优化
和重用
的理解?参考答案:
1.设置reuseIdentify,重用cell,减少创建cell1的内存靠小
2.提前计算cell的噶度并缓存,因为heightForRowAtIndexPath调用最频繁
3.不阻塞主线程,UITableview定制或者减速滑动结束的时候在进行异步加载图片(SDWebImage已经做到了)
4.有大量的圆角头像的时候不要使用cornerRadius和masksToBounds设置,因为离屏渲染很消耗性能
5.减少subviews的层次,使用不透明视图,可以极大提高渲染的速度:opaque = YES
6.如果只修改了某一行,就只更新单行
7.销毁的时候手动把代理设置为nil
圆角图片
如何优化
?参考答案:
有大量的圆角头像的时候 不要使用cornerRadius和masksToBounds设置,因为设置了这两个属性是 离屏渲染性能消耗较大,
可以通过 Core Graphics 画出圆角矩形( UIBezierPath画圆)
死锁
,如何防止
死锁
?参考答案:
所谓死锁,通常是指两个线程A和B都卡住了,并等待对方完成某些操作。
A不能完成时因为它在等待B完成。但B也不能完成,因为它在等待A完成。于是大家都完不成。就导致了死锁(DeadLock)
block
为什么要修饰为copy
?参考答案:
修饰为copy后对block里面的对象retain一次,防止里面的对象提前释放。
block
循环引用
,block
分配
在栈
还是堆
,什么情况下block会从栈区复制到堆区
?参考答案:
block有可能存在三个地方:
_NSConcreteStackBlock:
只有用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。
_NSConcreteMallocBlock
有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,
没有强指针引用即销毁,生命周期由程序员控制
_NSConcreteGlobalBlock
没有用到外界变量或只有到全局变量、静态变量的block为_NSConcreteGlobalBlock,
生命周期从创建到应用程序结束。
js
代码;js
调用
OC
的代码?参考答案:
shouldStartLoadWithRequest代理方法里面拦截
使用JavaScriptCore框架里面的JSContext
应用
之间如何唤起
?参考答案:
添加 Schema
[UIapplication sharedApplication]openURL 跳转
NSString
为什么用copy
修饰?参考答案:
一般情况下,我们都不希望字符串的值跟着原字符串的变化而变化,所有我们一般用copy来修饰NSString的属性。
JSPath
使用了iOS
的什么原理
?参考答案:
通过JS 调用和改写OC方法最根本的原因是Objective-C是动态语言。
OC上所有方法的调用/类的生成都通过 Objective-C Runtime在运行时进行。
drawRect
有什么影响?参考答案:
drawRect方法依赖Core Graphics框架来进行自定义的绘制,但是这种方法主要的缺点就是它处理touch事件的方式:每次按钮被点击后,都会用setNeddsDisplay进行强制重绘;而且不止一次,每次点击事件触发两次执行。
这样的话从性能的角度来说,对CPU和内存来说都是欠佳的。
特别是如果在我们的页面上有多个这样的UIButton实例。
C
和Objective-C
如何混用
?参考答案:
1.obj-c的编译器处理后缀为m的文件时,可以识别obj-c和c的代码,处理mm文件可以识别obj-c,c,c++代码,
但是cpp文件必须只能用c/c++代码,而且cpp文件include的头文件,也不能出现obj-c的代码,因为cpp只是cpp。
2.在mm文件中混合cpp直接使用即可。所以obj-c混cpp不是问题。
3.在cpp中混用obj-c其实就是使用obj-c编写的模块使我们想要的。
如果模块以类实现,那么要按照cpp class的标准来写类的定义,头文件不能出现obj-c的东西。包括#import cocoa的。
实现文件中,即类的实现代码中可以使用obj-c的东西,可以import,只是后缀是mm。
如果模块以函数实现,那么头文件要按c的格式声明函数,实现文件中,c++函数内部可以用obj-c,但后缀还是mm或m。
总结:只要cpp文件和cpp include的文件中不包含obj-c的东西就可以用了,cpp混用obj-c的关键是使用接口,而不能直接使用实现代码,实际上cpp混用的是obj-c编译后的o文件,这个东西其实无差别的,所以可以用。obj-c的编译器支持cpp。
静态链接库
的理解?参考答案:
静态链接库就是.a文件,相当于java里的jar包。
把一些类编译到一个包中,在不同的工程中如果导入此文件就可以使用里面的类,具体使用依然是#import"xxx.h" )。
fmmpeg框架
的理解?参考答案:
音视频编解码框架,内部使用UDP协议针对流媒体开发,内部开辟了六个断开来接受流媒体数据,完成快速接受之目的。
fmdb
框架的理解?参考答案:
数据库框架,对sqlite的数据操作进行了封装,使用户可以把精力都放在sql语句上面。
沙盒模型
Sandbox
?参考答案:
整个iPhone工程进行文件操作有此工程对应的指定的位置,不能逾越。
应用程序只能访问和操作位于沙盒中的资源。
沙盒模型
的四个文件夹
,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么?参考答案:
Documents目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其他应该定期备份的信息。
AppName.app目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
Library目录:这个目录下有两个子目录:Caches和Preferences。
Preferences目录包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefault类来取得和设置应用程序的偏好。
Caches 目录用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
json
解析方法
么,它们的底层
是如何处理
的?你了解么?参考答案:
json解析的用法,用框架的用法简单介绍:
底层原理遍历字符串中的字符,
最终根据格式规定的特定字符,
比如{}号,[]号,:号等进行区分,
{}号是一个字典的开始,
[]号是一个数组的开始,
:号是字典的键和值得分水岭,
最终乃是将json数据转化为字典。
字典中值可能是字典,数组,或字符串而已。
xml
解析
的原理
是什么,你还用过其他解析方式
么?参考答案:
NSXMLParser,
其他解析方式有自定义二进制解析,就是按字节去解析,电话会谈就如此。
还可以是字符串之间用特殊符号连接的数据,将此数据用特殊符号可以分割成所用数据。
大型项目
的时候,如何进行内存泄露检测
的?参考答案:
可以通过xcode的自带工具run—start with performance tool里面有 instruments 下有个leaks工具。
启动此工具后,运行项目,工具里可以显示内存泄露的情况。双击可以找到源码位置。可以帮助进行内存泄露的处理。
物体
从界面
中的一点运动
到另一点
,有哪些方法?参考答案:
四种方式:
1.beginAnimation
2.线程
3.NStimer
4.图层动画(路径)
person对象
的retain
count
分别是多少?参考答案:
Person *person = [[Person alloc]init]; count 1
[person retain]; retain count 2
[person release]; retain count 1
[person release]; retain count 0
self.name = "object"
和 name = "object"
有什么不同吗?参考答案:
self.name = "object" 会调用对象的 setName()方法。会使object引用计数加1 name = "object" 会直接把 object赋值给当前对象的name属性。引用计数不增加。
@synthesize
、@dynamic
的理解?参考答案:
@synthesize是系统自动生成getter和setter属性声明;
@synthesize的意思是,除非开发人员已经做了,否则由编译器生成相应的代码,以满足属性声明;
@dynamic 是开发者自己提供相应的属性声明;
@dynamic意思是由开发者人员提供相应的代码;
对于只读属性需要提供getter。
对于读写属性需要提供 setter和 getter。
设计模式
?聊聊你所知道的设计模式。参考答案:
设计模式是为了特定场景下的问题而定制的解决方案。
特定场景指问题所在的重复出现的场景,问题指特定环境下你想达成的目标。
同样的问题在不同的环境下会有不同的限制和挑战。
定制的解决方案是指在特定环境下克服了问题的限制条件而达成目标的一种设计。
设计模式分为三种类型。共23种:
创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)、访问者模式。
参考答案:
首先从逻辑结构上说,两者都是数据结构的一种,
但存在区别
数组是申请的一块持续的内存空间,并且是在编译阶段就要确定空间大小的。同时在运行阶段是不允许改变的,所以它不能够随着需要的改变而增大或减少空间大小,
所以当数据量大的时候。有可能超出了已申请好的数组上限。
产生数组越界,或者是数据量很小,对于没有使用的数组空间,造成内存浪费。
链表则是动态申请的内存空间,并不像数组一样需要事先申请好大小,链表是现用现申请就OK,根据需要动态的申请或删除内存空间,对于的是增加和删除数据。
所以比数组要灵活。
再从物理存储即内存分配上分析:
数组是连续的内存,
对于访问数据,可以通过下标直接读取,时间复杂度为O(1),
而添加和删除数据就比较麻烦,需要移动操作数所在位置后的所有数据。时间复杂度为O(N)。
链表是物理上非连续的内存空间,
对于访问数据,需要从头遍历整个链表直到找到要访问的数据,没有数组有效,
但是在添加和删除数据方面,只需要直到操作位置的指针,很方便可以实现增删,较数组比较灵活有效率。
所以综合以上,
对于快速访问数据,不经常有添加删除操作的时候选择数组实现,
而对于经常添加删除数据,对于访问没有很高的要求的时候选择链表。
顺序查找
算法的看法?参考答案:
条件: 无序或有序队列
原理:按顺序比较每个元素,直到找到关键字为止。
时间复杂度:O(n)
二分查找(拆半查找)
算法的看法?参考答案:
条件:有序数组
原理:查找过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;
如果某一特定元素大于或者小于中间元素,则在数组大于或者小于中间元素的那一半中查找,
而且开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都会使搜索范围缩小一半。
时间复杂度:O(logn)
二叉树查找
算法的看法?参考答案:
条件:先创建二叉排序树
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则左子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序数。
原理:在二叉查找树b中查找x的过程为:
若b是空树,则搜索失败,否则:
若x等于b的根节点的数据域之值,则查找成功,否则
若x小于b的根节点的数据域之值,则搜索做子树;否则:
搜索右子树。
哈希表法(散列表)
的看法?参考答案:
条件:先创建哈希表(散列表)
原理:根据键值方式
(key value)进行查找,通过散列函数,定位数据元素。
时间复杂度:几乎是O(1),取决于产生冲突的多少。
分块查找
算法的看法?参考答案:
原理:将n个数据元素“按块有序”划分为m块(m<=n)。 每一块的结点不必有序,但是块与块之间必须“按块有序”; 即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字; 而第2块中任一元素又都必须小于第3块中的任一元素,然后使用二分查找及顺序查找。
UITableviewCelll
上有个UILabel
,显示NStimer
实现的秒表事件,手指滚动cell
过程中,label
是否刷新
,为什么?参考答案:
这是否刷新取决于timer加到RunLoop中的Mode 是什么。
Mode 主要是用来指定事件在运行循环中的优先级,分为NSDefaultRunLoop (KCFRunLoopDefaultMode):默认,空闲状态
UITrackingRunLoopMode :ScrollView滚动时会切换到该Mode
UIInitializationRunLoopMode :RunLoop启动时,会切换到该Mode
苹果公开提供的Mode有两个:
NSDefaultRunLoopMode
(KCFRunLoopDefaultMode)
NSRunLoopCommonModes
(KCFRunLoopCommonModes)
在编程中:如果我们把一个NSTimer对象以NSDefaultRunLoopMode添加到主运行循环中的时候,ScrollView滚动过程中会因为Mode 的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度。那就应该使用默认模式。
但是,如果希望在滚动时,定时器也要回调,那就应该使用NSRunLoopCommonModes
weak
关键字,相比assign
有什么不同?参考答案:
在ARC中,在有可能出现循环引用的时候,往往要通过让其中一端使用weak来解决。
比如:delegate代理属性。自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,
自定义IBOutlet控件属性一般也是使用weak,当然,也可以使用strong。
不同点:
weak此特质表明该属性定义了一种“非拥有关系”(nonowning relationship)。
为这种属性设置新值时。设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到摧毁时。属性值也会清空(nil out)。
而assign 的"设置方法"只会执行针对“纯量类型”(scalar type,例如 CGFloat 或者NSInteger等)的简单赋值操作。
assign可以用非OC对象,而weak必须用于OC对象。
copy
关键字?参考答案:
用途:
NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为它们有对应的可变类型:
NSMutableString、NSMutableArray、NSMutableDictionary;
block也经常使用copy关键字,block使用copy是从MRC遗留下来的“传统”,在MRC中,
方法内部的block是在栈区的,使用copy可以把它放到堆区,在ARC写不写都行:
对于block使用copy还是strong效果是一样的,但写上copy也无伤大雅,还能时刻提醒我们:编译器自动对block进行了copy操作。
如果不写copy,该类的调用者有可能忘记或者根本不知道“”编译器会自动对block进行了copy操作“。
他们有可能在调用之前自行拷贝属性值,这样操作多余而低效。
copy
)NSMutableArray *array?参考答案:
两个问题:
1.添加,删除修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃,
因为copy就是复制一个不可变的NSArray的对象;
2.使用了atomic属性会影响性能;