Region-based notifications aren't always triggered immediately when the edge of the boundary is crossed. The system applies heuristics to ensure that the boundary crossing represents a deliberate event and is not the result of spurious location data. For more information about the heuristics that are applied, see Monitoring the User's Proximity to Geographic Regions.
10【问题】【C++】以下输出结果是什么?分析原因
class A{
public:
int m;
void foo1(){ cout<<“hello”<<endl;}
void foo2(){cout << “hello”<< m <<endl;}
virtual void foo3(){cout << “hello”<<endl;}
};
A*p =NULL;
p->foo1();
p->foo2();
p->foo3();
(一)使用 beginBackgroundTaskWithExpirationHandler 函数,向系统申请一段时间来执行需要后台运行的操作,这种方法的缺点是,后台操作最多只能运行10分钟,超过10分钟之后App会休眠。使用这种方法需要APPNAME-info.plist中设置 Application does not run in background 为NO,然后在适当的时间调用 beginBackgroundTaskWithExpirationHandler 函数。
(二)将 App 的后台运行模式设置为 audio 、VOIP、location、Newstand 等,无限制的在后台运行。修改 info.plist -> requried background modes-> App plays audio or streams audio/video using AirPlay , 进入后台,播放无声音乐。前台后台一套下载流程,下载完成,更新数据;下载失败,重新下载尝试。这种方案审核风险较大,不建议使用。因为审核时是可以通过静态分析知道使用了哪些API的,如果一个程序本来就不是音乐类的,却使用了播放音乐的API后台播音乐,有可能就被拒绝,如果想要绕过这个限制,可以向APP增加播放音乐的功能,但这样实际是增加了无用功能。
iTeaTime(技术清谈)【-002期】【代号:模仿游戏之寻龙诀】
本期特辑:iOS应用安全与逆向之基础原理 本期出品人:微博@iOS程序犭袁 本期代号:模仿游戏之寻龙诀 本期出题人(排名不分先后):
注:题目难度五星为满分,各个类目下题目从易到难依次排列。
逆向类
1【问题】【iOS】对称加密有哪些?描述其原理。 【难度🌟】【出题人:风扬-拍拍贷-SOi】
2【问题】【iOS】随便找一个正在运行的程序,给
objc_msgSend
下符号断点。程序断下之后,写一条 lldb 指令打印当前调用方法的selector
【难度🌟🌟】【出题人:鹅喵-便利蜂移动端】【回答】 要看具体 cpu 架构 读取第二个参数对应的寄存器:
objc_msgSend
两个固定参数,selector
第二个,存放在x0
、x1
寄存器中。【SAGESSE-iOS-深圳】给出答案:
po (char*)$rsi
po (char*)$x1
po (char*)$arg2
arm
和在x86_64
下用po (char*)$arg2
都能得到预期效果。$arg1
-$argN
是第1到第n,无论什么框架和约定(lldb处理过了),:arg1 到 argN 是 Xcode9/10 添加的功能,在 Xcode8 或者之前需要用rsi
和x1
。【鹅喵-便利蜂移动端】写成
x/s $arg2
也行,x/s
比较好打po (char*)$arg2
或者po x/s $arg2
。/one more thing/
上述讨论与调用约定有关,相关概念:
rdi
、rsi
、rdx
、rcx
、r8
、r9
参考文档:
3【问题】【iOS】应用中使用了一个外部动态库的符号,这个符号的具体实现被查找了几次?为什么? 【难度🌟🌟🌟】【出题人:鹅喵-便利蜂移动端】
【提示】看过
fishhook
原理应该都知道lazy symbol binding
。4【问题】【iOS】Objective-C 和 C 在 iOS 中的区别, 尝试从以下角度分析两者区别:
【难度🌟🌟🌟】【出题人:风扬-拍拍贷-SOi】
/one more thing/
【出题人提示】 汇编角度: 汇编调用OC和C的过程分别是什么 2、加载区别: OC:消息转发
自定义C函数:直接调用地址 系统C函数:共享缓存库 3、存储区别:MochO-中存储的区别
//TODO: 待讨论部分 第二点感觉覆盖不全,自己写的C代码就是调用库了吧?
【答案】
5【问题】【汇编】arm64位系统里的通用寄存器有多少个,分别是干什么的?什么是状态寄存器? 【难度🌟🌟🌟】【出题人:风扬-拍拍贷-SOi】
6【问题】【iOS】动态下发一个经过苹果签名的动态库是否可以加载(允许使用dlopen), 为什么? 【难度🌟🌟🌟🌟】【出题人:SAGESSE-iOS-深圳】
【出题人提示】 1: 首先,动态下载肯定不在bundle里, 只能在沙盒里面 2: 从题目条件得知是经过苹果签名的,所以代码签名这一步是通过的 3: 可以通过lldb测试
7【问题】【iOS】iOS 是如何通过代码签名确保应用安全的? 【难度🌟🌟🌟🌟】【出题人:SAGESSE-iOS-深圳】
8【问题】【iOS】分别给 objc_msgSend 和你要调用的方法下符号断点,第一次断下的时候可以在调用栈中看到 objc_msgSend。第二次断下的时候,调用栈里只有你要调用的方法了。objc_msgSend 哪儿去了? 【难度🌟🌟🌟🌟】【出题人:鹅喵-便利蜂移动端】
【提示】其实是想讨论一下如何控制堆栈平衡以及
backtrace
背后的原理:【答案】
跳板函数/蹦床函数/trampoline function 正解。
首先,如果用了 bl 指令的话 lr 里面会存储返回地址的,但是串成串还要靠栈帧结构。
x86_64
有个一个rbp
, 每一次 call 都会压入rbp
, 就会形成一帧帧调用栈, 然后通过rbp
可以朔到最开始。所以展示backtrace
只要遍历栈帧就可以了,首个栈帧靠 ip 确定。普通的函数是call调用, 而objc_msgSend 是长跳转(jump),估计是为了减轻栈溢出的压力, 另外尾递归也是长跳转
Q-A环节 Q:[腾讯-刘翅鹏]第8题是指定cachelookup吗? A:[鹅喵-便利蜂移动端]不是,CacheLookup 里面应该是顺带做了这个事
参考 《[腾讯-刘翅鹏]的笔记》
Q:是否如下图所言:
A: [鹅喵-便利蜂移动端]是的,不过 cdecl 调用约定是被调用者清理栈空间,所以纯汇编写的时候要注意平衡
A:【欧阳大哥】上面那个红框中的结论有待商榷吧。 如果函数调用发生在最后一条指令时不能用bl而只用用b的原因是因为:执行bl指令时会把当前指令的下一条指令保存到LR寄存器中。问题是因为这是最后一条指令了,下一条指令是一条未知指令,所以如果仍然用bl指令的话,那么函数返回时所跳转的地址将可能是一条无效的地址了。。而不是所谓的栈溢出的现象。
Q:[SAGESSE-iOS-深圳] 我突然想到个问题,栈顶的第一个条数据是rbp吗, A:[鹅喵-便利蜂移动端]应该是栈上最后一个申请的变量,rbp 是当前栈帧的顶端,所以函数退出清理栈的时候不需要记住你申请了多少栈空间,只要 movq %rbp, %rsp 就可以了。*(rbp + 8) 是上个栈帧的 rbp。
然后通过返回地址可以就可得到函数的信息
Q: 也就是说:第一个调用的函数,当前栈的rbp是啥?
*(rbp)
。A: 刚才调试了一下,是个0.
Q-A结束
常规类
9【iOS】kengny 是一名产品经理,他们的 app 是一款类似美团的产品,最近他和一些店家进行了PY交易,要求用户到他们家店附近的时候,立即收到通知。 小地和大风哥,会上听到需求后,小地立即说:这个需求做不了。大风哥会上没说话,产品经理说,明天上线,怎么实现我不管,散会。
会后,大风哥悄悄说对小地说要做也可以,可以这样做:___。
请补充填空,要求给出详细理由,包括技术实现细节,如有必要贴出示例代码。
【 难度🌟】【出题人 微博@iOS程序犭袁】
已知 iOS 定位方法有:GPS定位、基站蜂窝定位、Wi-Fi定位等多种定位方法,
精准度优先级可以为:
如果结合以上多种定位方法,这四个方案是同时的,组合起来可有效命中率。
蓝牙相关的例子: 好多超市有蓝牙定位,还有商场的室内定位,都是基于蓝牙
还有基于Apple设备蓝牙配对效果的:
其中Wi-Fi SSID部分注意事项:
即使申请了权限,也只能在系统的Wi-Fi列表里获取所有Wi-Fi信息,APP内好像也是不能获取的。
正如 《iOS NetworkExtension 框架使用笔记》 所说:
此类的APP也是有引导用户这么做的,第一次不行,需要重新进一次吧: 而且有人反应该权限现在申请好像比较难,周期较长,半个月还不一定能申请好。
涉及的API:
GPS 部分采用地理围栏相关的API:
适合横向的大范围坐标,但比如写字楼、商场等纵向的情景,纯定位不是很理想。需要借助其他措施。
UNLocationNotificationTrigger 部分的限制:
10【问题】【C++】以下输出结果是什么?分析原因
【难度🌟🌟】【出题人:欧阳大哥-美团-北京】
【答案】
【SAGESSE-iOS-深圳】:
错误分析: foo1和foo2是静态成员函数,调用时编译器会直接生成调用地址, 因为不会访问this指针所以调用不会出现问题;但foo2有访问成员m的操作,这就需要访问this指针了所以会出现段错误; foo3因为是虚函数,所以需要访问虚表,但this是空指针,所以调用也会出现段错误;
输出结果:
因为c++是一个跨平台的语言,在每个平台的 STL 实现都有可能不一样,所以输出结果会有所不同。 在foo2中的
cout << "hello" << m <<endl;
会被拆分成:当执行到
cout << m;
时,会因为this->m
而发生段错误,这时的"hello"
到底有没有输出呢?第一种情况: 在 Xcode 中直接运行,这时
stdout
直接输出到 Xcode 的控制台,它的输出是即时的, 所以有溃之前就己经输出,所以最终输出结果是第二种情况: 在 Xcode 中编译,然后在终端中运行,这时输出不是即时的,写入的数据是缓存在内存里,只有当调用
endl(flush)
时才会真正的去写入文件(io),所以最终输出结果是以下为其他同学的讨论部分:
//TODO: 待系统整理
[鹅喵-便利蜂移动端]:
原因:[鹅喵-便利蜂移动端]cpp的方法调用等价于method(this,args...),只要不访问成员变量或vtable就不会崩
[SAGESSE-iOS-深圳]foo1和foo2是静态成员函数,调用时编译器会直接生成调用地址, foo3因为是虚函数,所以需要访问虚表,但p是空指针,所以会直接段错误, 顺便一提如果foo1和foo2有访问成员m的操作结果又不一样了
【Never-成都-太合乐动-iOS】: foo1 和 foo2 直接地址 调用
【yx@美团北京】: foo2的hello确实也能正常输出 一直到尝试访问m实例变量挂了
看你用的g++ 编译器的行为可能不一样
编译器不重要,实现在STL库里面
xcode debugger的控制台输出是即时的
实测没有 重点是没有执行到endl
foo3 因为是虚函数 会牵扯到 虚表操作
Xcode 可以输出,xcode debugger帮助flush了。
std::endl
一定会刷,其他情况一般不会立即刷。以下为不同环境的输出结果:
11【iOS】CoreData中几个核心概念及关系阐述下,第三方库 MagicRecord 的读写操作是在什么线程中执行的?为何有人如此讨厌使用CoreData,CoreData适合什么样的项目?【 难度🌟🌟🌟】【出题人 微博@iOS程序犭袁】
12【iOS】kengny 是一名产品经理,他平时有两大爱好:第一,到处在各类群里求买企业证书,第二,运营着一款小成本的视频 app,迫于成本压力,一般只会有两个人参演。他向大风哥提出需求,说希望能够在用户退到后台后,上传日志记录用户什么时候进入的后台,便于记录用户使用时长。并要求退到后台后依然能够下载小视频,这样用户上班点击下载按钮,回到家躺床上打开 APP 就能看了。并且要求把后台下载成功率定为大风哥的KPI。
如果你是大风哥,你将如何应对。必要时贴出示例代码。
【 难度🌟🌟🌟】【出题人 微博@iOS程序犭袁】
【答案】
关于后台下载,我们研究的时候,很有必要把 iOS7 和 iOS7 之后的方案分清楚。 因为即使是现在,我们的 APP 最低版本都是iOS9+,但现在网上很多答案都还是iOS7之前的方案,给读者产生了混淆与误导。
现在分几个部分解答:
iOS7前的陈旧方案
iOS7 之前后台下载方案十分不灵活,现在已经基本不再使用。下面做下介绍:
beginBackgroundTaskWithExpirationHandler
函数,向系统申请最多10分钟来执行需要后台运行的操作下面做下详细介绍:
(一)使用
beginBackgroundTaskWithExpirationHandler
函数,向系统申请一段时间来执行需要后台运行的操作,这种方法的缺点是,后台操作最多只能运行10分钟,超过10分钟之后App会休眠。使用这种方法需要APPNAME-info.plist中设置Application does not run in background
为NO,然后在适当的时间调用beginBackgroundTaskWithExpirationHandler
函数。(二)将 App 的后台运行模式设置为 audio 、VOIP、location、Newstand 等,无限制的在后台运行。修改
info.plist
->requried background modes
->App plays audio or streams audio/video using AirPlay
, 进入后台,播放无声音乐。前台后台一套下载流程,下载完成,更新数据;下载失败,重新下载尝试。这种方案审核风险较大,不建议使用。因为审核时是可以通过静态分析知道使用了哪些API的,如果一个程序本来就不是音乐类的,却使用了播放音乐的API后台播音乐,有可能就被拒绝,如果想要绕过这个限制,可以向APP增加播放音乐的功能,但这样实际是增加了无用功能。总结:
第二种,只有极少数 APP 能够用到,一般 APP 无法使用;第一种,运行时间无法保证,无法进行下载恢复等操作,毫无实用性可言。故现在已经很少 APP 在使用上述方式。下面介绍更为实用的方案:
用 Background Transfer service 特性实现大文件后台下载
注意:
NSURLSession
是一个类蔟,不同系统版本实现上是有差异的。很多都要亲自实验一下,而且不同系统版本的行为也不太一样。建议不仅要阅读文档,而且要多多尝试调试。当 App 使用了 Background Transfer service特性后,可以将一个下载任务交给系统的独立进程去下载,不管App在前台、休眠、以及crash,下载过程都在进行,因为是系统的独立进程在为App进行下载。
基本步骤:
使用
backgroundSessionConfigurationWithIdentifier:
初始化的 configure 初始化一个后台下载使用的 session。 实现NSURLSessionDelegate
、NSURLSessionTaskDelegate
、NSURLSessionDownloadDelegate
中的URLSession:task:didCompleteWithError:
,URLSession:downloadTask:didFinishDownloadingToURL:
,urlSessionDidFinishEvents(forBackgroundURLSession:)
和其他业务需求的 protocol 实现application:handleEventsForBackgroundURLSession:completionHandler:
方法。点击下载,创建 task 对象并开始:
后台下载成功调用相关代理方法,实现数据和 UI 更新;下载失败从 error 中查找 resumeData,重新开始下载。
QA环节:
Q:下载能控制退到后台的下载速度么,在后台慢慢下,打开在前台全速
A:有的,configureation 里面有一个属性值 discretionary,就是控制的,在后台不占用设备性能的情况下进行下载。
Q:如果在前台,设了这个discretionary的值有用么,也会被系统控制下载速度?
A:没用了,里面强调的是 allows background tasks,如果需要具体限速的数值的话,是没有的,需要自己实现了。限速如果实现,应该是类似断点续传的思路。用suspend和resume做。
Q:后台下载是要用户开的吧?关掉了就只能用播放音乐了。URLSessionDownloadTask 和后台刷新开关有没有关系?
A:URLSessionDownloadTask 和后台刷新开关没有关系。同时,后台下载跟应用的进程没有关系了,是系统做维护的。,用第一种方案也是可以实现,即使用户手动关闭的app,最终也是可以下载成功的,但也不是说还会继续后台下载。
(Apple-Developer-Documentation-API-NSURLSessionConfiguration-discretionary )
此属性设置为 YES 时,系统根据当前性能自动处理后台任务的优先级,以获得最佳性能 (仅background session有效)。根据文档可知:
allowsCellularAccess
和discretionary
被用于节省通过蜂窝连接的带宽。建议在使用后台传输的时候,使用discretionary
属性,而不是allowsCellularAccess
属性,因为它会把 Wi-Fi 和电源可用性考虑在内。QA环节结束
用户主动关闭的app,会保存
resumedata
,在后面启动的时候调用didCompleteWithError
方法,error
里有resumedata
,可以持续下载。 这里有一个缺点就是,iOS11之前,如果因为没有网络导致系统下载失败了,系统即使唤醒了App,App也是没有办法下载的,然后App会进入休眠,即使后面有了网络,系统也不会继续下载,因为只要系统向App发出了失败的信号,除非App 调用resume函数来恢复下载过程,系统是不会自己恢复下载的。这里就需要用到本文提到的BAR ( Background App Refresh)模式,让App过一段时间被系统唤醒,然后App就可以去检查网络,当有网时恢复下载过程,恢复下载的原理类似于断点下载。iOS11之后,可以采用下面API进行:
URLSession Adaptable Connectivity API
iOS11的重大更新,可以通过
urlSession(_:taskIsWaitingForConnectivity:)
让请求等待网络正常后再自动尝试。URLSessionTask Scheduling API
通过
URLSessionTask Scheduling API
可以在 App 没有运行的时候下载内容,而手机也会结合实际电量,使用状态去决定是否执行。参考:
用 BAR ( Background App Refresh)或 Remote notification 实现小文件后台下载
在iOS7以后,系统增加了两种后台的模式,一种是 Background fetch ,另一种是Remote notification,可以用于小文件下载。
BAR ( Background App Refresh):
之前讨论的方案,跟后台刷新没有关系的,没有使用
performFetchWithCompletionHandler
相关的功能注意:BAR ( Background App Refresh)相关的,用户没有主动kill掉的app才会有 BAR 功能。 具体用法参考:
Remote notification:
在iOS7以前,当系统收到推送消息后,会立即弹出消息提示用户,用户点击消息之后,就可以启动App,然后加载数据。使用了这种新的后台模式之后,当系统收到推送消息之后,会唤醒App,给App一个机会执行一部分操作,等操作之后才提醒用户,而且还支持 silent 模式,即执行完操作之后,完全不对用户做任何提醒,默默的就在后台把活干完了,此功能需要用户开启推送权限。
13【iOS】大风哥负责企业内部员工 APP 的iOS开发工作,产品经理 kengny 老师通知说,老板要求,发布2.0,对员工数据进行更新,在 iOS 原有数据库基础上,增加一个字段,用于记录用户 “是否是兄弟”。该字段只有老板有操作权限,如果打开APP后,发现不是兄弟,就弹出离职申请页面。服务端得知填写完成后,会发送指令要求手机原地爆炸。如果不能爆炸的话,远程删除APP,或将手机初始化也可以。
如果你是大风哥你将如何应对。要求数据库操作贴出示例代码,数据库类型不限。
【 难度🌟🌟🌟】【出题人 微博@iOS程序犭袁】
14【算法】请通过编程实现大数(亿位)的相加减乘除。(不限语言) 【 难度🌟🌟🌟】【出题人 消摇-金融-深圳iOSqp】 【提示】用人算的思路让电脑去算。
/one more thing/
多人提供答案:
[Lefex]提供以下答案:
《图解一道面试题 - 大数相加减乘除》
【SAGESSE-iOS-深圳】提供以下答案:
Swift版本:
除法使用移位减实现,不过会这么复杂是因为是以二进制保存的数:
[李胜运-齐数-上海小程序]提供的JS版本,仅仅实现了正数的加 乘。
//TODO: 待完善。
//TODO: 未完待续
15 【iOS】多线程操作中,读写操作一定要在同一线程中执行吗?给出原因,并至少给出两种场景佐证你的观点,以及实现方法。【难度🌟🌟】【出题人 微博@iOS程序犭袁】
16 【iOS】一个app中可能会产生几个 Autorelease Pool , Autorelease Pool 中的临时对象,何时会被dealloc 。给出原因。【难度🌟🌟】【出题人 微博@iOS程序犭袁】
17【iOS】For in 循环中频繁创建临时变量的场景下,如何使用 Autorelease Pool 优化, 着重讲下你放置pool的位置,以及这些临时变量的生命周期改变。并给出原因。【难度🌟🌟🌟】【出题人 微博@iOS程序犭袁】
18【算法】【iOS】在一个字典中含有,字符串,字典,数组。层层嵌套,可能十几层。现在想知道任意节点Value中是否含有某个字符串。【 难度🌟🌟🌟🌟】【出题人 BM-成都iOS】 【提示】广度优先,深度优先,为非常朴素的暴力搜索。 暴力搜索也有策略的,看到数组、字典就展开, 这是 深度优先 看到数据、字典先记下来,等这一层所有节点都查完了再展开下一层的,这是广度优先。从数据结构来分析,深度优先是维护一个栈,广度优先是维护一个队列
One more thing...
【非礼勿视】以下为彩蛋部分,建议28岁以上男性观看
/one more thing/