前言:考核基础有没有盲区,是否有意识写出正确代码
001 循环引用的原因,它会造成怎样的后果以及如何避免?
循环引用的原因是对象在作用域结束后无法回收,会造成内存泄漏。
1.NSTimer 在创建的时候会默认对当前self有个强引用,这里可以用一个weakself对象进行持有,在dealloc方法中,一定要先使用[self.timer invalidate];
然后将timer=nil;
timerWithTimeInterval创建的timer需要手动添加到runloop中,而scheduledTimerWithTimeInterval创建的timer会自动添加到runloop中,是DefaultMode,但是在一个tableview的上放置的timer,那需要timerWithTimeInterval来创建,手动添加到NSRunLoopCommonModes中
2.block中使用对象需要在block外面进行__weak将self转化成weakself,避免循环引用;在block函数作用域中需要将weakself中一个局部__strong变量进行转化,为了让在作用域范围之后被回收。
3.Delegate,一般在声明delegate的时候都要使用弱引用weak
002 我们一般开发会用到CoreFundation对象,这些非指针对象,这些在ARC管理下会有一些编译问题,可能做一些处理,你能说一下原因么?
这个意思就是说当我们用到一些比较古老的MRC代码的时候如何在ARC下运行起来
在MRC代码会爆出'retain' is unavailable: not available in automatic reference counting mode
可以在Xcode编译进行调整targets---build phase--complie sources里找到对应的文件添加flag
开启:-fobjc-arc 关闭-fno-objc-arc
targets--build settings 搜automatic reference counting; yes --开启 no--关闭
其中的原因是ARC=LLVM+runtime
003 为什么要使用关联对象?
它使得可以在Runtime为某个类对象绑定一个对象
属性在分类中编译器是不能生成的,需要关联对象手动生成.具体如下:
@interface MyObject (set)
@property (nonatomic, assign) NSInteger pageValue;
@end
#import "MyObject+set.h"
#import <objc/runtime.h>
@implementation MyObject (set)
- (NSInteger)pageValue{
return [objc_getAssociatedObject(self, @selector(pageValue)) intValue];
}
- (void)setPageValue:(NSInteger)pageValue{
objc_setAssociatedObject(self, @selector(pageValue), @(pageValue), OBJC_ASSOCIATION_ASSIGN);
}
@end
在分类中,因为累的实力变量的布局以及固定,使用@property已经无法向固定的布局中添加新的实例变量(有可能会覆盖子类的实例变量),使用关联对象
objc_getAssociatedObject和objc_setAssociatedObject两个方法来模拟构成属性的三个要素。
三要素:
生成实例变量;生成getter方法--property;生成setter方法--setProperty
004 GCD里有个优先级,这个优先级是怎么体现的?
dispatch_set_target_queue(serialQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
GCD 串行 并发队列中线程是怎样执行的
007 OC提供哪些工具或者方法来解决线程不安全?
1.atomic 修饰符,它只是保证了属性的setter和getter方法内部是线程安全的。
2.锁 如下图各种所得消耗性能
008 锁--一般加锁会有开销,开销成本体现在哪里?
枷锁的目的是保证共享资源在任意时间里,只有一个线程访问,这样就可以避免多线程导致共享数据错乱的问题
两个常见的锁:互斥锁和自旋锁
互斥锁加锁失败后,线程会释放CPU,给其他线程
自旋锁加锁失败后,线程会忙等待,直到它拿到锁
对于互斥锁佳作失败而阻塞的现象,是由操作系统内核实现的。互斥锁加锁失败后会从余台陷入内核态,让内核帮忙切换线程。
存在一定开销成本。(切换上下文的成本)
- 当线程加锁失败时,内核会把线程的状态从「运行」状态设置为「睡眠」状态,然后把 CPU 切换给其他线程运行;
- 接着,当锁被释放时,之前「睡眠」状态的线程会变为「就绪」状态,然后内核会在合适的时间,把 CPU 切换给该线程运行。
如果你能确定被锁住的代码执行时间很短(内存管理中自旋锁引用计数+-1),就不应该用互斥锁,而应该选用自旋锁,否则使用互斥锁。
自旋锁是通过CAS函数(Compare and Swap)包含两个步骤
1.查看锁的状态,如果锁是空闲的,则执行第2步;如果不是空闲就在这里等待
2.将锁设置成当前线程持有
009 UIView和CALayer 的联系和区别?
- 联系
每一个 view 中都有一个 layer,view 持有并管理这个 layer,且这个 view 是 layer 的代理。
- 区别
UIView
负责响应事件,参与响应链,为 layer 提供内容。
CALayer
负责绘制内容,动画。
事件响应链
hit-test寻找hit-test view 也就是触摸事件所在的view
发生触摸事件后系统将事件放在一个由UIApplication管理的队列中,UIApplication将事件发送给KeyWindown,KeyWindown在视图树中寻找一个最适合的view来响应事件
1.view自身是否能够接受触摸事件,
2.触摸点是否在自己范围内
3.从后往前遍历subviews,重复执行1和2
如果没有找到符合条件的子试图,自己就是最合适的
但是需要保证视图 isUserInteractionEnabled 为 YES,isHidden 为 NO,alpha 大于 0.01。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
通过 Hit-testing
找到了触摸 view 之后。调用这个 view 的 touches 方法来做具体处理:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
如果这个 view 调用了 [super touches]
方法,则事件顺着响应者链向上传递(子视图向父视图)。
其顺序为view-->super--->superview-->viewController-->rootViewController-->window-UIApplication
如果到最后UIApplication都没有处理,则丢弃。
App在处理交互事件时会创建新的UIView,也会修改现有的View,然后就会更新约束,调整布局,再渲染并显示
0010 如果我用CAAnimation做一个动画,类似于一个红包雨场景,从上到下这个运动过程,当运动到中间的时候,红包view的frame,指向的是哪里?
CALayer层 考核是否使用过CAAnimation动画交互场景
每个 CALayer 有一个 presentationLayer 属性,当CAAnimation发生时,你在屏幕上看到的实际上是 presentation layer 的改变。如果你访问 presentation layer,QuartzCore 将会计算现有的帧状态,并且使用这个帧状态去构建 presentation layer 对象。因为动画状态在动画执行期间一直处于改变,因此你将会获得近似值。
在GitHub上搜红包关键字 很多demo
隐式动画 修改layer可动画属性 系统完成的
显式动画 是创建CAAnimation对象提交到Layer执行的动画。
0011 界面卡顿的一些情况,界面卡顿的原因是什么?
在一个16.6毫秒的垂直信号到来之前CPU和GPU的工作没有完成
CPU层面:
1.尽量用轻量级对象例如K线用CASharpLayer
2.不要频繁调用UIView 的相关属性
3.提前计算好布局,例如朋友圈炸好评排布
4.Autolayout会比直接设置frame消耗更多CPU资源
5.图片的size最好与UIImageView的size保持一致
6.控制一下线程的最大并发量
7.尽量把耗时的操作异步子线程中,执行完成之后返回到主线程进行数据刷新操作
GPU层面:
1.尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示
2.GPU能处理的最大纹理尺寸是4096X4096,一旦超过这个尺寸,就会占用GPU资源进行处理
3.尽量减少视图的数量和层次
4.减少透明度的视图,不透明就设置opaque为YES
5.尽量避免离屏渲染
怎么解决阴影圆角渲染造成的卡顿问题?
UIBezierPathh 画 CAShapeLayer
// Create a mask layer.
CAShapeLayer *maskLayer = [CAShapeLayer new];
maskLayer.frame = self.bounds;// Define our path, capitalizing on UIKit's corner rounding magic.
UIBezierPath *newPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.layer.cornerRadius];maskLayer.path = newPath.CGPath;// Apply the mask.
self.layer.mask = maskLayer;
0012 一般开发的时候会用到约束,怎么做给界面添加的约束是刚刚好的?
NSLayoutConstraint
0013 http和https区别?
0014 https 中证书校验过程?
https://juejin.im/post/6844903545272041479
0015 算法题
本地机子上有2K-3K个通讯录,远端也有2K-3K个通讯录,怎么比较快的找到这两个集合之间差别的部分,它的时间复杂度是怎样的?
O(N+M)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set; // 存放结果
unordered_set<int> nums_set(nums1.begin(), nums1.end()); //nums1 存入set中
for (int num : nums2) {
// 发现nums2的元素 在nums_set里又出现过
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);
}
}
return vector<int>(result_set.begin(), result_set.end());
}
};
参考文献: