线程队列关系
https://www.jianshu.com/p/abeaaf3c3dd1
main函数之前走了几步
https://www.jianshu.com/p/1d2bef8a87e4
https://www.jianshu.com/p/e6a80ebaf6bb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
简单的回答:
1.动态库链接库
2.ImageLoader加载可执行文件, 里边是被编译过的符号,代码等
3.runtime与+load
性能优化
https://www.jianshu.com/p/fe566ec32d28
https://mp.weixin.qq.com/s/jN3jaNrvXczZoYIRCWZs7w
内存优化
内存泄露检测
https://www.jianshu.com/p/9fc2132d09c7
ios网络优化
https://www.jianshu.com/p/a470ab485e39
https://www.cnblogs.com/ziyi--caolu/p/8058577.html
https://www.infoq.cn/article/how-ctrip-improves-app-networking-performance/#
synchronized 开发 https://www.cnblogs.com/feng9exe/p/7233349.html
ios 重签名机制 https://www.jianshu.com/p/47facc920fb5
http://blog.cnbang.net/tech/3386/
atomc https://cloud.tencent.com/developer/article/1445940 为什么不是线程安全
http 原理 https://www.jianshu.com/p/14cd2c9d2cd2
性能优化 https://www.jianshu.com/p/95df83780c8f
instruments- 通过leaks分析内存泄露 性能调优工具 Instruments 之 TimeProfiler
https://blog.csdn.net/lichuandev/article/details/79636363
Assets和文件 区别http://www.manongjc.com/article/68443.html
tcp udp https://blog.csdn.net/wuhuagu_wuhuaguo/article/details/78507826
https://segmentfault.com/a/1190000020053709
layer 内部维护着三分 layer tree,分别是 presentLayer Tree(动画树),modeLayer Tree(模型树), Render Tree (渲染树),在做 iOS动画的时候,我们修改动画的属性,在动画的其实是 Layer 的 presentLayer的属性值,而最终展示在界面上的其实是提供 View的modelLayer
I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面)
P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)
B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别(具体比较复杂,有4种情况),换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累~
afnetwork 做了什么
http://www.cocoachina.com/articles/18277
https://www.jianshu.com/p/3506b9babdac
https://juejin.im/post/5d1efe16f265da1baa1e944d
特别好 afnetwork 原理 https://www.jianshu.com/p/488c1f46cedd
特别好 afnetwork https://www.jianshu.com/p/856f0e26279d
https://www.jianshu.com/p/f32bd79233da
sdwebimage
https://blog.csdn.net/jacky_jin/article/details/73312810
http://www.cocoachina.com/articles/16869
https://msd.misuland.com/pd/3107373619924174644
https://www.jianshu.com/p/b8517dc833c7
https://www.jianshu.com/p/fd02749bd44a
https://www.cnblogs.com/liuyang666/p/5288599.html
https://www.cnblogs.com/liuyang666/p/5288599.html
ios底层
https://www.jianshu.com/notebooks/24110540
block https://www.jianshu.com/p/a41396ff0247
categoty 后编译的执行
dispatch_once 源码http://lingyuncxb.com/2018/02/01/GCD源码分析2%20——%20dispatch-once篇/
gcd 源码 http://lingyuncxb.com/categories/iOS/
Crash 防护系统 https://www.jianshu.com/p/e3713d309283
https https://www.jianshu.com/p/c73839a3ed18
KVC的底层实现?
当一个对象调用setValue方法时,方法内部会做以下操作:
1). 检查是否存在相应的key的set方法,如果存在,就调用set方法。
2). 如果set方法不存在,就会查找与key相同名称并且带下划线的成员变量,如果有,则直接给成员变量属性赋值。
3). 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值。
4). 如果还没有找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
如何高性能的给 UIImageView 加个圆角?
不好的解决方案:使用下面的方式会强制Core Animation提前渲染屏幕的离屏绘制, 而离屏绘制就会给性能带来负面影响,会有卡顿的现象出现。
self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;
正确的解决方案:使用绘图技术
- (UIImage *)circleImage {
// NO代表透明
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
// 获得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加一个圆
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextAddEllipseInRect(ctx, rect);
// 裁剪
CGContextClip(ctx);
// 将图片画上去
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
return image;
}
还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给UIImageView 添加了的圆角,其实也是通过绘图技术来实现的。
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
答卓同学ios面试题
2018 iOS面试题
ios保持流畅性的技巧
1> 如果当前这个view是控制器的view,那么控制器就是上一个响应者
2> 如果当前这个view不是控制器的view,那么父控件就是上一个响应者
响应者链的事件传递过程:
1>如果当前view是控制器的view,那么控制器就是上一个响应者,事件就传递给控制器;如果当前view不是控制器的view,那么父视图就是当前view的上一个响应者,事件就传递给它的父视图
2>在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理
3>如果window对象也不处理,则其将事件或消息传递给UIApplication对象
4>如果UIApplication也不能处理该事件或消息,则将其丢弃
事件处理的整个流程总结:
1.触摸屏幕产生触摸事件后,触摸事件会被添加到由UIApplication管理的事件队列中(即,首先接收到事件的是UIApplication)。
2.UIApplication会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口(keyWindow)。
3.主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。(至此,第一步已完成)
4.最合适的view会调用自己的touches方法处理事件
5.touches默认做法是把事件顺着响应者链条向上抛。
touches的默认做法:
备战九十 ios面试题菜单
四、RunLoop的实现机制
对于RunLoop而言最核心的事情就是保证线程在没有消息的时候休眠,在有消息时唤醒,以提高程序性能。RunLoop这个机制是依靠系统内核来完成的(苹果操作系统核心组件Darwin中的Mach)。
RunLoop通过mach_msg()函数接收、发送消息。它的本质是调用函数mach_msg_trap(),相当于是一个系统调用,会触发内核状态切换。在用户态调用 mach_msg_trap()时会切换到内核态;内核态中内核实现的mach_msg()函数会完成实际的工作。 即基于port的source1,监听端口,端口有消息就会触发回调;而source0,要手动标记为待处理和手动唤醒RunLoop
Mach消息发送机制 大致逻辑为: 1、通知观察者 RunLoop 即将启动。 2、通知观察者即将要处理Timer事件。 3、通知观察者即将要处理source0事件。 4、处理source0事件。 5、如果基于端口的源(Source1)准备好并处于等待状态,进入步骤9。 6、通知观察者线程即将进入休眠状态。 7、将线程置于休眠状态,由用户态切换到内核态,直到下面的任一事件发生才唤醒线程。
一个基于 port 的Source1 的事件(图里应该是source0)。
一个 Timer 到时间了。
RunLoop 自身的超时时间到了。
被其他调用者手动唤醒。
8、通知观察者线程将被唤醒。 9、处理唤醒时收到的事件。
如果用户定义的定时器启动,处理定时器事件并重启RunLoop。进入步骤2。
如果输入源启动,传递相应的消息。
如果RunLoop被显示唤醒而且时间还没超时,重启RunLoop。进入步骤2
10、通知观察者RunLoop结束。
1、加速启动时间。快速打开app是很重要的,特别是用户第一次打开它时,对app来讲,第一印象太太太重要了。你能做的就是使它尽可能做更多的异步任务,比如加载远端或者数据库数据,解析数据。避免过于庞大的XIB,因为他们是在主线程上加载的。所以尽量使用没有这个问题的Storyboards吧!一定要把设备从Xcode断开来测试启动速度
2、使用Autorelease Pool。NSAutoreleasePool`负责释放block中的autoreleased objects。一般情况下它会自动被UIKit调用。但是有些状况下你也需要手动去创建它。假如你创建很多临时对象,你会发现内存一直在减少直到这些对象被release的时候。这是因为只有当UIKit用光了autorelease pool的时候memory才会被释放。消息是你可以在你自己的@autoreleasepool里创建临时的对象来避免这个行为。
3、选择是否缓存图片。常见的从bundle中加载图片的方式有两种,一个是用imageNamed,二是用imageWithContentsOfFile,第一种比较常见一点。
4、避免日期格式转换。如果你要用NSDateFormatter来处理很多日期格式,应该小心以待。就像先前提到的,任何时候重用NSDateFormatters都是一个好的实践。如果你可以控制你所处理的日期格式,尽量选择Unix时间戳。你可以方便地从时间戳转换到NSDate:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return[NSDate dateWithTimeIntervalSince1970:timestamp];
}
这样会比用C来解析日期字符串还快!需要注意的是,许多web API会以微秒的形式返回时间戳,因为这种格式在javascript中更方便使用。记住用dateFromUnixTimestamp之前除以1000就好了。
平时你是如何对代码进行性能优化的?
利用性能分析工具检测,包括静态 Analyze 工具,以及运行时 Profile 工具,通过Xcode工具栏中Product->Profile可以启动,
比如测试程序启动运行时间,当点击Time Profiler应用程序开始运行后.就能获取到整个应用程序运行消耗时间分布和百分比.为了保证数据分析在统一使用场景真实需要注意一定要使用真机,因为此时模拟器是运行在Mac上,而Mac上的CPU往往比iOS设备要快。
为了防止一个应用占用过多的系统资源,开发iOS的苹果工程师门设计了一个“看门狗”的机制。在不同的场景下,“看门狗”会监测应用的性能。如果超出了该场景所规定的运行时间,“看门狗”就会强制终结这个应用的进程。开发者们在crashlog里面,会看到诸如0x8badf00d这样的错误代码。
优化Table View
正确使用reuseIdentifier来重用cells
尽量使所有的view opaque,包括cell自身
如果cell内现实的内容来自web,使用异步加载,缓存请求结果 减少subviews的数量
尽量不适用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果
使用rowHeight, sectionFooterHeight和sectionHeaderHeight来设定固定的高,不要请求delegate
UIImage加载图片性能问题
imagedNamed初始化
imageWithContentsOfFile初始化
imageNamed默认加载图片成功后会内存中缓存图片,这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象.如果缓存中没有找到相应的图片对象,则从指定地方加载图片然后缓存对象,并返回这个图片对象.
imageWithContentsOfFile则仅只加载图片,不缓存.
加载一张大图并且使用一次,用imageWithContentsOfFile是最好,这样CPU不需要做缓存节约时间.
使用场景需要编程时,应该根据实际应用场景加以区分,UIimage虽小,但使用元素较多问题会有所凸显.
不要在viewWillAppear 中做费时的操作:viewWillAppear: 在view显示之前被调用,出于效率考虑,方法中不要处理复杂费时操作;在该方法设置 view 的显示属性之类的简单事情,比如背景色,字体等。否则,会明显感觉到 view 有卡顿或者延迟。
在正确的地方使用reuseIdentifier:table view用 tableView:cellForRowAtIndexPath:为rows分配cells的时候,它的数据应该重用自UITableViewCell。
尽量把views设置为透明:如果你有透明的Views你应该设置它们的opaque属性为YES。系统用一个最优的方式渲染这些views。这个简单的属性在IB或者代码里都可以设定。
避免过于庞大的XIB:尽量简单的为每个Controller配置一个单独的XIB,尽可能把一个View Controller的view层次结构分散到单独的XIB中去, 当你加载一个引用了图片或者声音资源的nib时,nib加载代码会把图片和声音文件写进内存。
不要阻塞主线程:永远不要使主线程承担过多。因为UIKit在主线程上做所有工作,渲染,管理触摸反应,回应输入等都需要在它上面完成,大部分阻碍主进程的情形是你的app在做一些牵涉到读写外部资源的I/O操作,比如存储或者网络。 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 选择一个子线程来执行耗时操作 dispatch_async(dispatch_get_main_queue(), ^{ // 返回主线程更新UI }); });
在Image Views中调整图片大小 如果要在UIImageView中显示一个来自bundle的图片,你应保证图片的大小和UIImageView的大小相同。在运行中缩放图片是很耗费资源的.
讲讲你用Instrument优化动画性能的经历吧(别问我什么是Instrument)
Apple的instrument为开发者提供了各种template去优化app性能和定位问题。很多公司都在赶feature,并没有充足的时间来做优化,导致不少开发者对instrument不怎么熟悉。但这里面其实涵盖了非常完整的计算机基础理论知识体系,memory,disk,network,thread,cpu,gpu等等,顺藤摸瓜去学习,是一笔巨大的知识财富。动画性能只是其中一个template,重点还是理解上面问题当中CPU GPU如何配合工作的知识。
facebook启动时间优化
1.瘦身请求依赖 2.UDP启动请求先行缓存 3.队列串行化处理启动响应
多线程死锁
https://www.jianshu.com/p/0d96f03597f6
https://www.jianshu.com/p/1f1b9516c631
kvo底层实现
https://www.jianshu.com/p/d6faa91a9721
ios组件化。pod 私有库
https://www.jianshu.com/p/5b52b49d2392
https://cloud.tencent.com/developer/article/1419756
category和extension
https://www.jianshu.com/p/dc3196030a03
fhttps://www.jianshu.com/p/4546f22b2e96
1 动态解析对象方法时,会调用+(BOOL)resolveInstanceMethod:(SEL)sel方法。
2 动态解析类方法时,会调用+(BOOL)resolveClassMethod:(SEL)sel方法。
class_addMethod 添加方法
消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector
{
// 返回能够处理消息的对象
if (aSelector == @selector(driving)) {
return [[Car alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
3消息签名
methodSignatureForSelector
如果methodSignatureForSelector方法返回正确的方法签名就会调用forwardInvocation方法,forwardInvocation方法内提供一个NSInvocation类型的参数,NSInvocation封装了一个方法的调用,包括方法的调用者,方法名,以及方法的参数。在forwardInvocation函数内修改方法调用对象即可。
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(driving)) {
// return [NSMethodSignature signatureWithObjCTypes: "v@:"];
// return [NSMethodSignature signatureWithObjCTypes: "v16@0:8"];
// 也可以通过调用Car的methodSignatureForSelector方法得到方法签名,这种方式需要car对象有aSelector方法
return [[[Car alloc] init] methodSignatureForSelector: aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
//NSInvocation 封装了一个方法调用,包括:方法调用者,方法,方法的参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// [anInvocation getArgument: NULL atIndex: 0]; 获得参数
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation中封装了methodSignatureForSelector函数中返回的方法。
// 此时anInvocation.target 还是person对象,我们需要修改target为可以执行方法的方法调用者。
// anInvocation.target = [[Car alloc] init];
// [anInvocation invoke];
[anInvocation invokeWithTarget: [[Car alloc] init]];
}
三次握手四次挥手https://blog.csdn.net/eddyjoe/article/details/87936084
ios 用信号量控制多个异步网络请求 https://www.cnblogs.com/YuFly-lyx/p/9056378.html
设计模式 https://www.jianshu.com/p/0fb8502e9129
nstime 循环引用 https://www.jianshu.com/p/823ef4fb63bc
界面卡顿检查 https://www.jianshu.com/p/b04b72ea5e83
卡顿 检测 https://www.jianshu.com/p/15d8462718c9
catch 缓存 https://juejin.im/post/5cf3a3f46fb9a07eae2a4708
自动移除 kvo https://www.cnblogs.com/fengmin/p/8125870.html
数据结构
https://www.cnblogs.com/xingmuxin/p/11304359.html
https://cloud.tencent.com/developer/article/1450113
LinkNode* Reverse(LinkNode* header)
{
if (header == NULL || header->next == NULL)
{
return header;
}
LinkNode* pre = header, *cur = header->next;
pre->next = NULL;
while(cur != NULL)
{
auto next = cur-> next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
】node* reverseList(node* H)
{
if (H == NULL || H->next == NULL) //链表为空或者仅1个数直接返回
return H;
node* p = H, *newH = NULL;
while (p != NULL) //一直迭代到链尾
{
node* tmp = p->next; //暂存p下一个地址,防止变化指针指向后找不到后续的数
p->next = newH; //p->next指向前一个空间
newH = p; //新链表的头移动到p,扩长一步链表
p = tmp; //p指向原始链表p指向的下一个空间
}
return newH;
}
排序https://www.runoob.com/w3cnote/sort-algorithm-summary.html
冒泡优化https://www.cnblogs.com/zsh-blogs/p/11255507.html
算法https://blog.csdn.net/bluesliuf/article/details/89043746