又到了不得不想换家公司的时间了。不是不想长待,各种心酸就不排解了,下面是几次面试的题目,次序不分先后。稍后会一一给自己解释
1.线性表:顺序表和链表优缺点,增删改查插入如何操作,在内存上是如何处理的;
2.表(UITableView)视图的重用机制,如何实现;
3.集合视图(UICollectionView)瀑布流如何实现及优化;
4.cell自适应高度的处理方式及优化;
5.loadView、awakeFromNib和layoutSubView、drawRect、setNeedsLayout、setNeedDisplay、layoutIfNeeded等方法何时调用;
6.如何使用cocoaPods管理第三方;
7.如何快速更换第三方类库;
8.轮播图的实现以三张图片为例;
9.简述APNS,及远程和本地推送的实现;
10.GCD的作用,适当说明使用过程;
11.简述HTTP协议错误代码的含义;
12.GET和POST的理解和区别;
13.热修复的方式及如何实现;
14.bug的搜集管理;
15.如何对接口数据进行缓存;
16.简述SDWebImage的实现原理;
17.AF和SD是如何管理线程的,如何实现线程安全;
18.简述自己知道的加锁方式;
19.简述几大线程方式及区别;
20.对于常用动画你的理解和使用;
21.简述delegate和block,及block对于变量的修改问题;
22.谈谈性能优化;
23.instruments你用了哪些功能,如何检测内存泄露;
以上如此多的问题,不论哪个都是一大块的知识,毕竟个人技能树上没点过的,还有很多,根本不敢谈一个技能点,被点满的情况。还好面试一般都是需要简述即可,点到即止。
又想吐槽今年下半年的遭遇了,再次忠告自己或他人,珍视生命,远离自诩创业公司无休止无报酬无赞赏的公司。好了说正题:
1.线性表:顺序表和链表优缺点,增删改查插入如何操作,在内存上是如何处理的;
这个问题是我近期的第一家面试公司问的,因为面试的那些天一直在公司通宵加班,根本没有时间补充正规水军的套路,回答的时候也是啃啃巴巴的,不过也算是回答到点上了。但是面试官预期的答案应该是这样的:
定义
1.线性表:零个或者多个数据元素的有限序列。
2.顺序表和链表是线性表的两种物理存储方式,
3.顺序表:用地址连续的存储单元来存放数据元素的数据结构;
4.链表:链表是一种物理存储单元上非连续、非顺序的数据结构;
不同点
1.存储空间上
顺序表是物理地址连续的数据结构;
链表是一种物理地址非连续的数据结构、
2.增删改查上(时间上)
顺序表:
正因为物理存储结构的不同,其表现形式不同,导致了其功能上的不同;
增:准确说是插入的一种,只是插入的位置是最后;
删:若删除位置后面还有元素,后边元素全部前移一位;
插入:若所插入位置后有元素,后面元素分别后移一位;
查:获取位置之后直接取;
改:查到之后,修改数据域数据;
从功能实现看其优缺点:
顺序表的存读时间复杂度是O(1);
插入或者删除的时候时间复杂度是O(n);
顺序表优点:(主要用于查询)
(1)快速存取任意位置元素;
(2)无需为了表示元素之间逻辑关系而增加存储空间(相对链表而言);
顺序表缺点:
增、删、改操作
链表优点:(主要用于增删该)
(1)增删该时只需要修改指针;
(2)不需要事先明确表长;
链表缺点:
查:链表的查询需要从前向后扫描;
2.表(UITableView)视图的重用机制,如何实现;
reuseIdentifier
tableView创建的时候,会新建一个复用池保存着创建的cell,而cell的复用标识符就是reuseIdentifier,不同标识符的cell是不同类的cell。通过dequeueReusableCellWithIdentifier:方法获取cell。
重用--><对象池设计模式>
重用cell的创建有两种方法:
初始化:没有注册的情况下,当重用池取出的cell为空时,需要初始化cell,并标注其reuseIdentifier。
注册:
纯代码注册:代码注册
xib注册:代码注册
storyBoard注册:不需要代码注册
3.集合视图(UICollectionView)瀑布流如何实现及优化;
瀑布流实现的正确姿势就是自定义UICollectionViewLayout,当然自定义的时候需要实现以下内部方法
1. - (CGSize)collectionViewContentSize;(主要返回cell的size)
2.- (void)prepareLayout;(预布局,每次刷新都会走的方法)
3.- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;(获取cell的布局属性数组)
4.- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;(根据indexPath设置cell布局属性)
/**
* 初始化
*/
- (void)prepareLayout {
[super prepareLayout];
self.contentHeight = 0;
//清除以前计算的所有高度
[self.columnHeights removeAllObjects];
for (NSInteger i = 0; i < self.columnCount; i++) {
[self.columnHeights addObject:@(self.edgeInsets.top)];
}
//清除之前所有布局属性
[self.attrsArray removeAllObjects];
//开始创建每一个cell对应发布局属性
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < count; i++) {
//创建位置
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
//获取indexPath位置cell对应的布局属性
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attrsArray addObject:attrs];
}
}
/**
* 决定cell的布局
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return self.attrsArray;
}
/**
* 返回indexPath位置cell对应的布局属性
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
//创建布局属性
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//collectionView的宽度
CGFloat collectionViewW = self.collectionView.frame.size.width;
//设置布局属性的frame
CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount;
CGFloat h = [self.delegate waterflowlayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
//找出高度最短的那一列
NSInteger destColumn = 0;
CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
for (NSInteger i = 0; i < self.columnCount; i++) {
//取得第i列的高度
CGFloat columnHeight = [self.columnHeights[i] doubleValue];
if (minColumnHeight > columnHeight) {
minColumnHeight = columnHeight;
destColumn = i;
}
}
CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
CGFloat y = minColumnHeight;
if (y != self.edgeInsets.top) {
y += self.rowMargin;
}
attrs.frame = CGRectMake(x, y, w, h);
//更新最短那列的高度
self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
//记录内容的高度
CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];
if (self.contentHeight < columnHeight) {
self.contentHeight = columnHeight;
}
return attrs;
}
- (CGSize)collectionViewContentSize {
return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);
}
- (CGFloat)rowMargin {
if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
return [self.delegate rowMarginInWaterflowLayout:self];
}else {
return SMDefaultRowMargin;
}
}
- (CGFloat)columnMargin {
if ([self.delegate respondsToSelector:@selector(columnMarginInWaterflowLayout:)]) {
return [self.delegate columnMarginInWaterflowLayout:self];
}else {
return SMDefaultColumnMargin;
}
}
- (NSInteger)columnCount {
if ([self.delegate respondsToSelector:@selector(columnCountInWaterflowLayout:)]) {
return [self.delegate columnCountInWaterflowLayout:self];
}else {
return SMDefaultColumnCount;
}
}
- (UIEdgeInsets)edgeInsets {
if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) {
return [self.delegate edgeInsetsInWaterflowLayout:self];
}else {
return SMDefaultEdgeInsets;
}
}
优化(tableView优化参考)
1.通过正确的姿势设置reuseIdentifier重用cell,headerView,footerView;
2.尽量减少不必要的透明View;
3.尽量避免渐变、拉伸、offScreen渲染;(maskToBouds)
4.一般情况下,处理数据的时候把对应高度计算好,封进Model中,避免重复计算;
5.展示网络数数据时,异步加载(一般都这样干的)
6.如果设置shadow,使用shadowPath;
7.尽量减少subView,如果subView多变,考虑异步绘制或者重写drawRect;(drawRect慎用)
8.cellForRow中的逻辑如果需要处理数据,请缓存结果;
9.对于数据结构慎重对待,针对不同业务处理使用合适数据结构,避免性能消耗;(一般都这么干了)
10.对于不同的height,size,请perpare并且cache;
11.cell在使用代理,block,target-action等操作时,防止循环引用;(weak,weak-Strong Dance,weak)
4.cell自适应高度的处理方式及优化;
我的处理方式是这样的:cell自适应是根据数据来的,那么在数据Model中建立对应属性并LazyLoad。
5.loadView、awakeFromNib和layoutSubView、drawRect、setNeedsLayout、setNeedDisplay、layoutIfNeeded等方法何时调用;
1.loadView()
调用顺序:view->loadViewIfRequired->loadView
[super loadView];重载:加载指定xib--(否)-->加载同名xib--(否)-->创建空白View
当重指向self.view的时候,可以在当前方法中设置;
该方法只能重载不能主动调用;
2. awakeFromNib()
方法由nib loading machinery 发出,当调用loadNibFile时,完成初始化设置和链接,并且在所有关联的对象唤醒该方法。
当一个视图或者控制器加载多个nib文件时,方法会被调用多次,所以当重写该方法时需要注意;
3.layoutSubViews()
事件的调用时机:
(1)addSubView:之后,并且子视图有frame,
(2)在有子视图的情况下,设置父视图size;
(3)更新子视图size时,当且仅当size不同时,才会调用子视图的该方法;
(4)滚动scrollView会触发该方法;
(5)init的时候不会触发该方法
4.drawRect()
(1)view的初始化没有设置rect时,方法不被调用。控制器中该方法调用在viewDidLoad之后;
(2)sizeToFit()之后会调用该方法;
(3)当view属性contentMode为UIViewContentModeRedraw。每次设置frame都会调用该方法;
(4)调用setNeedsDisplay,或者setNeedsDisplayInRect:时,如果view的frame设置不为0,都会调用该方法;
注意:
(1)不可以显示调用
(2)实时画图时,不可用gestureRecognizer,只能用touchBegin等方法来调用setNeedsDisplay,实现调用drawRect;
5. setNeedsLayout
在receiver标上一个需要被重新布局的标记flag,在系统runloop的下一个周期自动调用layoutSubviews;
6.setNeedDisplay
在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iphone device的刷新频率是60hz,也就是1/60秒后重绘;
7.layoutIfNeeded
遍历的不是superview链,应该是subviews链;
如果,有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews);
如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局;
在视图第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded];
6.如何使用cocoaPods管理第三方;
要点:
(1)cocoaPods的安装;
(2)Podfile的创建、编辑、install;
7.如何快速更换第三方类库;
简单来说用第三方的正确姿势必须要自己封装下再使用,否则后期维护特别麻烦,修改的地方太多了。对于面试来说,直接提下封装的优点,一般就可以了;
8.轮播图的实现以三张图片为例;
自己实现:
这里给出思路:第一张图片前加一张最后一张的图片,目前有四张,在最后位置添加第二张图片;可以在滚动首尾的时候,减速方法中切换(视觉效果),保证正常轮播;
第三方:SDCycleScrollView
9.简述APNS,及远程和本地推送的实现;
APNs全名是Apple Push Notification Service。
远程推送
1.deviceToken
该字段是在应用注册通知服务时APNS返回的设备唯一标示。
2.远程推送流程
一般应用在前台时主要走应用服务器和应用的socket长连接。这样消息的传递要比APNS快,此时一般不需要deviceToken;应用处于后台时,长连接中断,应用服务器会将标识信息和要发送的信息以一定模板发送给APNS,APNS将消息发送给具有标识的用户;
3.本地推送
流程:创建本地通知、设置属性、加入本地通知调度池、(iOS8.0以后)获取授权。
10.GCD的作用,适当说明使用过程;
GCD主要用在后台执行较慢任务;延迟执行任务;以及在后台任务中,切换回主线程,更新UI;
对比:
pthread:通用于Unix/Linux/Windows的C语言线程管理API,可移植性强,但是使用繁琐,需要使用者管理线程生命周期;
NSThread:使用Objective-C实现,轻量级的线程管理,但是也需要手动管理线程的生命周期;
NSOperation:基于GCD,使用Objective-C实现的面向对象的线程管理,比GCD更高级,但是处理简单任务会比GCD代码更多;
竞争&同步:两个线程抢夺同一个资源,就会竞争,为了防止竞争,一个线程拥有资源的时候,会对资源加锁,另一个线程就要等待解锁以后再拥有这个资源,这叫同步。
死锁:两个线程互相等待对方释放资源;
主线程&后台线程:主线程也叫前台线程,程序启动的默认线程,操作UI的线程。后台线程,即非主线程,用于不影响主线程的完成一些任务;
并行&串行:并行,就是几个任务一起完成。串行,就是几个任务一个接着一个完成。
同步&异步:同步执行线程,等待新线程执行完以后,再继续执行当前线程,很少用到。异步执行线程,在执行新线程的同时,继续执行当前线程,常用。
11.简述HTTP协议错误代码的含义;
1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。
100 (继续) 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。
101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。
2xx(成功)表示成功处理了请求的状态代码。
200 (成功) 服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。
201 (已创建) 请求成功并且服务器创建了新的资源。
202 (已接受) 服务器已接受请求,但尚未处理。
203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。
204 (无内容) 服务器成功处理了请求,但没有返回任何内容。
205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。
206 (部分内容) 服务器成功处理了部分 GET 请求。
3xx(重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
300 (多种选择) 针对请求,服务器可执行多种操作。服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301 (永久移动) 请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304 (未修改) 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
305 (使用代理) 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
4xx(请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。
400 (错误请求) 服务器不理解请求的语法。
401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
403 (禁止) 服务器拒绝请求。
404 (未找到) 服务器找不到请求的网页。
405 (方法禁用) 禁用请求中指定的方法。
406 (不接受) 无法使用请求的内容特性响应请求的网页。
407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
408 (请求超时) 服务器等候请求时发生超时。
409 (冲突) 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息。
410 (已删除) 如果请求的资源已永久删除,服务器就会返回此响应。
411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。
413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。
415 (不支持的媒体类型) 请求的格式不受请求页面的支持。
416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。
417 (未满足期望值) 服务器未满足"期望"请求标头字段的要求。
5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。
500 (服务器内部错误) 服务器遇到错误,无法完成请求。
501 (尚未实施) 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。
12.GET和POST的理解和区别;
get请求和post请求都可以用于做获取数据请求,在请求数据安全方面post请求比get请求安全;
get是以明文的方式向服务器发送请求,post是包装到请求体body中后,在向服务器发送请求;
get请求的参数全部暴露在接口中,一般叫做明文请求或者傻瓜式请求,post请求的参数一般是以字典的方式进行拼接,相对于get比较安全;
如果从服务器获取数据,或者查询数据,使用get请求;如果上传数据到服务器或者修改服务器上传数据使用post请求;
get请求的URL在使用过程中,会限制长度,因此长度非常长的请求建议用post请求;
对文件大小的请求:get不允许向服务器上传文件(图片,pdf,音视频);
13.热修复的方式及如何实现;
热修复的相关框架还是很多的,简单来说主要是利用了iOS运行时特性。在运行时,调用修改的方法,达到热更新的目的;
14.bug的搜集管理;
一般常用bug分析方式有:
1.使用友盟、百度等第三方崩溃统计工具。
2.自己实现应用内崩溃收集,并上传服务器。
3.Xcode-Devices中直接查看某个设备的崩溃信息。
4.使用苹果提供的Crash崩溃收集服务。
15.如何对接口数据进行缓存;
轻量级<覆盖型>
1.Plist文件<初始化文件路径,数据写入路径writeToFile:> 可以使用plist的数据类型有 NSArray,NSDictionary,NSData,NSString,NSNumber,NSDate;
2.NSUserDefaults<存储一些简单的信息>及时更新存储synchronize;
3.NSKeyedArchiver<遵循NSCoping协议,归档解档>
重量级<关系型>
1.sqlite3直接编写splite3语言
2.coreData<迁徙>对于数据库的封装,图形化设计,提供了一种映射的存储关系;
16.简述SDWebImage的实现原理;
(1)入口 sd_setImageWithURL: placeholderImage: options: progress: completed:会先把 placeholderImage 显示,然后会把重复的请求删除掉, SDWebImageManager 根据 URL 开始处理图片,处理进度progress,处理完成complete;
(2)进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo: 交给 SDImageCache 从缓存查找图片是否已经下载queryDiskCacheForKey:delegate:userInfo:;
(3)先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存, SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager;
(4)SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片;
(5)如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存;
(6)根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:;
(7)如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片;
(8)如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:;
(9)共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片;
(10)图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败;
(11)connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果;
(12)connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理;
(13)图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多;
(14)在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader;
(15)imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成;
(16)通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片;
(17)将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程;
(18)SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片;
(19)SDWebImage 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用;
(20)SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
17.AF和SD是如何管理线程的,如何实现线程安全;
1.AF并没有为每个请求创建一个线程,而是将每个请求封装成一个NSOperation放到queue中,当operation执行时,都会在一个单独线程中创建NSURLSession对象,并用KVO监听所有回调。下面是值得借鉴的地方
(1)并发粒度:因为AF的所有网络请求都在operationQueue中,而该queue会有多个并发线程来执行,最大的并发线程数一般是系统根据硬件信息默认的,AF则留有设置最大并发线程数的接口;
(2)block优化:如果某一个operation的success和failure的回调占用较多CPU,则可以创建一个任务队列并赋值给该operation的completionQueue;
2.SDWebImage中每个图片的下载都是一个Operation操作,每个operation在单独线程中创建自己的NSURLSession对象,并通过观察者模式,发送加载状态的广播,以便对加载进行管控。
18.简述自己知道的加锁方式;
1. @synchronized 关键字加锁(最耗时)
2. NSLock 对象锁
3. NSCondition
4. NSConditionLock 条件锁(最耗时)
5. NSRecursiveLock 递归锁
6. pthread_mutex 互斥锁(C语言)
7. dispatch_semaphore 信号量实现加锁(GCD)(加锁时间第二快)
8. OSSpinLock(已不再安全)(加锁时间最快)
19.简述几大线程方式及区别;
1.pthread:跨平台可移植,线程需手动管理;
2.NSThread:面向对象,直接操作线程对象,需要手动管理线程生命周期,可以获取当前线程及是否为主线程;
3.GCD:充分利用设备多核,线程生命周期自动管理,使用之后不用管什么时候结束。
4.NSOperation:封装GCD,更加面向对象,线程生命周期手动管理。相对GCD可以实现更高级的管理功能。
20.对于常用动画你的理解和使用;
1.UIView动画
通过UIView支持的动画属性来实现动画效果,如frame,bounds,center,tranform,alpha,backgroundColor,contentStretch等,对于约束的修改则需要在block中调用 setNeedsLayout,而约束的改变在动画之后执行。
2.核心动画
核心动画采用Core Animation框架,直接作用在CALayer层,而不再UIView;
UIView与CALayer的主要区别
1、UIView是可以响应事件的,但是CALayer不能响应事件
2、UIView主要负责管理显示内容,而CALayer主要负责渲染和呈现。如果没有CALayer,我们是看不到内容的。
3、CALayer内部维护着三分layer tree,分别是presentLayer tree(动画树),modeLayer tree(模型树),render tree(渲染树),在做iOS动画的时候,我们修改动画的属性,在动画的其实是CALayer的present Layer的属性值,而最终展示在界面上的其实是提供UIView的modelLayer。
CALayer核心动画与UIView动画的区别:
UIView封装的动画执行完毕之后不会反弹,CALayer核心动画则会;另外UIView的动画期间可以处理用户事件,CALayer核心动画则不能。例如:如果是通过CALayer核心动画改变layer的位置状态,表面上看虽然已经改变了,但是实际上它的位置是没有改变的。
21.简述delegate和block,及block对于变量的修改问题;
delegate和block主要解决对象之间通信问题。都需要注意循环引用的问题,ARC下,delegate为weak修饰,block用copy修饰,防止block实现代码被释放掉。在block块内对外局部变量修改时,局部变量需要__block修饰。
22.谈谈性能优化;
1.性能优化:
(1)UITableView优化
(2)网络请求优化
(3)图片加载优化
2.代码优化:
(1)合理使用设计模式
(2)善于使用面向对象语言特性
(3)设计优化,缓存等
(4)代码规范
3.程序体积优化:
4.数据安全:
23.instruments你用了哪些功能,如何检测内存泄露;
1. Zombies:查看僵尸对象
2.Leaks:内存泄漏
3.Time Profiler:查看加载耗时