2018年5月28日 ios面试3 runloop
1.
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];” 这句到底做什么?
是让主线程runloop进入休眠吗?不能理解为什么加了这句,代码就不会往下执行了?
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入
https://blog.ibireme.com/2015/05/18/runloop/
https://blog.csdn.net/chenyong05314/article/details/14127767
4种mode
Cocoa定义了四中Mode
Default:NSDefaultRunLoopMode,默认模式,在Run Loop没有指定Mode的时候,默认就跑在Default Mode下
Connection:NSConnectionReplyMode,用来监听处理网络请求NSConnection的事件
Modal:NSModalPanelRunLoopMode,OS X的Modal面板事件
Event tracking:UITrackingRunLoopMode,拖动事件
Common mode:NSRunLoopCommonModes,是一个模式集合,当绑定一个事件源到这个模式集合的时候就相当于绑定到了集合内的每一个模式
https://www.cnblogs.com/czc-wjm/p/5901324.html
https://blog.csdn.net/gang544043963/article/details/79608129
非主线程的runloop
@implementation AFURLConnectionOperation @synthesize outputStream = _outputStream; + (void)networkRequestThreadEntryPoint:(id)__unused object { @autoreleasepool { [[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } }
http://www.cocoachina.com/ios/20150601/11970.html
Runloop接收两种源事件:input sources和timer sources。
input sources 传递异步事件,通常是来自其他线程和不同的程序中的消息;
timer sources(定时器) 传递同步事件(重复执行或者在特定时间上触发)。
性能
https://blog.csdn.net/opentogether/article/details/52447862
2.阿里
野指针
https://www.jianshu.com/p/4c8a68bd066c
锁
http://www.cocoachina.com/ios/20160707/16957.html
block copy
在MRC环境下,如果没有用__block,会对外部对象采用copy的操作,而用了__block则不会用copy的操作。
https://www.cnblogs.com/iOSJason/p/5605557.html
https://www.jianshu.com/p/d28a5633b963
1)synchronized性能
本文要说的就是最后一种 @synchronized 的用法。其实 @synchronized 用法很简单,首先 @synchronized() 小括号内需要一个参数,这个参数就表示信号量。这个参数可以是任何对象,包括 self,或者是自定义的信号量。针对不同的操作应该定义不同的信号量。
https://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/
https://www.jianshu.com/p/938d68ed832c#
2)sqlite锁
3)http2.0
4)urlsession
5)runloop的先决条件,为什么还需要循环
6)野指针
7)NSArray copy
https://www.jianshu.com/p/665e218c6ec1
8)block weak strong
9)block实现
10)类簇
- (void)aac_forwardInvocation:(NSInvocation *)anInvocation { @try { [self aac_forwardInvocation:anInvocation]; } @catch (NSException *exception) { [anInvocation invokeWithTarget:[_UnrecognizedSelectorProxy sharedInstance]]; NSString *reason = [NSString stringWithFormat:@"%@ %@",exception.name,exception.reason]; [AACManager recordCrashLogWithInstance:self type:AACTypeUnrecognizedSelector reason:reason]; } @finally { } }
阿里面试官问,try catch影响性能,已经判断了方法不存在,为什么还要try catch
https://github.com/tangbl93/AntiAPPCrash
6. 异常(Exceptions)
不要使用C++异常。
优点:
1) 异常允许上层应用决定如何处理在底层嵌套函数中发生的“不可能发生”的失败,不像出错代码的记录那么模糊费解;
2) 应用于其他很多现代语言中,引入异常使得C++与Python、Java及其他与C++相近的语言更加兼容;
3) 许多C++第三方库使用异常,关闭异常将导致难以与之结合;
4) 异常是解决构造函数失败的唯一方案,虽然可以通过工厂函数(factory function)或Init()方法模拟异常,但他们分别需要堆分配或新的“非法”状态;
5) 在测试框架(testing framework)中,异常确实很好用。
缺点:
1) 在现有函数中添加throw语句时,必须检查所有调用处,即使它们至少具有基本的异常安全保护,或者程序正常结束,永远不可能捕获该异常。例如:if f()
calls g()
calls h()
,h
抛出被f
捕获的异常,g
就要当心了,避免没有完全清理;
2) 通俗一点说,异常会导致程序控制流(control flow)通过查看代码无法确定:函数有可能在不确定的地方返回,从而导致代码管理和调试困难,当然,你可以通过规定何时何地如何使用异常来最小化的降低开销,却给开发人员带来掌握这些规定的负担;
3) 异常安全需要RAII和不同编码实践。轻松、正确编写异常安全代码需要大量支撑。允许使用异常;
4) 加入异常使二进制执行代码体积变大,增加了编译时长(或许影响不大),还可能增加地址空间压力;
5) 异常的实用性可能会刺激开发人员在不恰当的时候抛出异常,或者在不安全的地方从异常中恢复,例如,非法用户输入可能导致抛出异常。如果允许使用异常会使得这样一篇编程风格指南长出很多(译者注,这个理由有点牵强:-()!
http://www.cnblogs.com/vs2008_taotao/archive/2010/08/18/1801984.html
3.method swi
- 实例一:替换ViewController生命周期方法
- 实例二:解决获取索引、添加、删除元素越界崩溃问题
- 实例三:防止按钮重复暴力点击
- 实例四:全局更换控件初始效果
- 实例五:App热修复
- 实例六:App异常加载占位图通用类封装
- 实例七:全局修改导航栏后退(返回)按钮
https://www.jianshu.com/p/f6dad8e1b848
目前可以处理掉的crash类型具体有以下几种:
-
unrecognized selector crash
-
KVO crash
-
NSNotification crash
-
NSTimer crash
-
Container crash(数组越界,插nil等)
-
NSString crash (字符串操作的crash)
-
Bad Access crash (野指针)
-
UI not on Main Thread Crash (非主线程刷UI(机制待改善))
https://github.com/guoyuan94/CrashHopper/tree/59f8349af3667cbc2afd1fa4587802f765954b6c
[NSObject swizzleInstanceMethod:@selector(forwardingTargetForSelector:) with:@selector(hopper_forwardingTargetForSelector:)]; ... #pragma mark - Unrecognized selector crash - (id)hopper_forwardingTargetForSelector:(SEL)aSelector { if (!CRASHHOPPER_OPENING) { return [self forwardingTargetForSelector:aSelector]; } id obj = [self hopper_forwardingTargetForSelector:aSelector]; if (obj || ![self canForwardContinue] || [NSStringFromClass(self.class) hasPrefix:@"_"]) { return obj; } NSString *className = @"HopperClass"; if ([self isKindOfClass:NSClassFromString(className)]) { return [NSNull null]; } Class newClass = NSClassFromString(className); if (!newClass) { newClass = objc_allocateClassPair(NSObject.class, [className cStringUsingEncoding:NSUTF8StringEncoding], 0); objc_registerClassPair(newClass); } NSString *selString = NSStringFromSelector(aSelector); if (![self isSelctorAdded:selString inClass:newClass]) { NSString *className = NSStringFromClass(self.class); IMP imp = imp_implementationWithBlock(^(){ NSException *exception = [NSException exceptionWithName:@"Unrecognized selector" reason:[NSString stringWithFormat:@"-[%@ %@]", className, selString] userInfo:nil]; DEAL_CRASHHOPPERLOG }); class_addMethod(newClass, aSelector, imp, selString.UTF8String); } return [newClass new]; }
代码有bug。
不过通过block转换为IMP时,需要注意,block不需要SEL,所以在block中只有一个默认参数self。在我们写block时,第一个参数必须为self,当函数调用的时候才能正确接收参数。
imp_implementationWithBlock(^(id self){
https://www.jianshu.com/p/6dcb18161fbb
+ (BOOL)resolveInstanceMethod:(SEL)sel { const GPBDescriptor *descriptor = [self descriptor]; if (!descriptor) { return [super resolveInstanceMethod:sel]; } // NOTE: hasOrCountSel_/setHasSel_ will be NULL if the field for the given // message should not have has support (done in GPBDescriptor.m), so there is // no need for checks here to see if has*/setHas* are allowed. ResolveIvarAccessorMethodResult result = {NULL, NULL};
#import "GPBMessage_PackagePrivate.h"
@interface GPBMessage () {