原文连接ios
上周公众号发布的如下文章:git
本期知识小集的主要内容包括:github
做者:hite和落雁数据库
卡顿检测系统,用于检测 App 的主线程运行状况。在追求 N 个 9 奔溃以外,卡顿也是咱们极其重要运行指标。windows
很遗憾,世界上最好的免费 APM 平台 Fabric 却没有。而国内的 bugly、网易云捕等,都提供了相似的功能。以下图是云捕的卡顿功能。后端
提及来卡顿检测,技术原理很简单,下面是来自 bugly 的 QA 里的描述数组
iOS 卡顿检查的依据是监控主线程 Runloop 的执行,观察执行耗时是否超过预约阀值(默认阀值为3000ms) 在监控到卡顿时会当即记录线程堆栈到本地,在App从后台切换到前台时,执行上报。
复制代码
卡顿检测系统,这个大任务,能够分解为两部分:卡顿的检测 + 卡顿的展现和管理。缓存
卡顿的收集,有现成的代码,核心代码可查看,gist gist.github.com/hite/1a7ee4…bash
检测到以后,须要获取当前时刻的堆栈,全部线程的堆栈(其实只须要主线程就够了)。服务器
OK,拿来主义,一个颇有名 PLCrashReporter
。我本身集成测试,在个人老古董机器,iPhone 6 跑,卡顿是能检测到,可是整个软件基本不可用,整个界面全卡住了。PLCrashReporter 生成日志代码以下图所示。
性能很是差,彻底不可用。
拿不到堆栈信息,没法展现,因此只能采用造轮子的方式。根据戴铭 blog 里的例子,我改造了下,如 gist gist.github.com/hite/1a7ee4… 所示。咱们收集到了全部堆栈的栈顶地址;接下来咱们须要将这些栈信息符号化。
很容易想到的方案是传到本身的服务器上,用 Mac 环境处理堆栈的符号化,转换为可读的堆栈数据——代价太大,并且还不经济。
在浏览了 fabric 的各项 API 后,我发现有一个很讨好的接口,recordCustomExceptionName
fabric 提供了 recordCustomExceptionName 接口,接口签名以下图所示。
咱们利用这个接口,将第一步收集的堆栈数据传给 fabric,让 fabric 给咱们符号化,并且 fabric 卡顿日志还可以聚合、分类、分组、跟踪。crash 日志的那一套均可以用上, fabric 用户对此是熟悉的。核心代码以下图所示。
至此,咱们用不多的代价就作好了一个卡顿检测的系统,而且和奔溃功能一块儿使用,集中处理 APM 各项指标。
这个方案在月活几十万的 App 应用了快半年了。用户一点都没有感觉到有卡顿系统,对现有系统影响很小。以下图所示。
全部的数据都在 crashlytics 里,选择 non-fatal,便可,从统计下来的数据来看,卡顿主要体验在;
基本上和咱们猜测是一致的,接下来就须要跟踪和处理这些卡顿。
目前这个系统有两个缺陷:
recordCustomExceptionName
接口的限制,全部线程的栈都被合并到一个栈,但不影响核心卡顿代码的阅读。做者: 高老师很忙
今天分享一个轻松的小知识点~~~
搜索了网上关于 iOS 定位的文章,不少在 locationManager(_:didUpdateLocations:)
收到回调就执行了 stopUpdatingLocation()
,以下图:
然而在一些状况之下,这样写是有隐患的(以下图),
在某次运行的时候(并非每次出现),在 21 点 16 分返回了一个 21 点 09 分的点,这是由于 CoreLocation
可能会返回一个缓存的值给咱们,因此咱们使用的时候应该判断一下时间戳(以下图),这样能够减小定位误差。
参考连接:O网页连接
做者: KANGZUBIN
在开发中,咱们常常须要获取设备的型号(如 iPhone X,iPhone 8 Plus 等)以进行数据统计,或者作不一样的适配。但苹果并无提供相应的系统 API 让咱们直接取得当前设备的型号。
其中,UIDevice 有一个属性 model 只是用于获取 iOS 设备的类型,如 iPhone,iPod touch,iPad 等;而其另外一个属性 name 表示当前设备的名称,由用户在设置》通用》关于》名称中设定,如 My iPhone,xxx 的 iPhone 等。然而,咱们没法根据这两个值得到具体的型号。
不过,每一种 iOS 设备型号都有对应的一个或多个硬件编码/标识符,称为 device model 或者叫 machine name,以前的小集介绍过,咱们能够经过以下图中的代码来获取。
因此,一般的作法是,先获取设备的 device model 值,再手动映射为具体的设备型号(或者直接把device model 值传给后端,让后端去作映射,这样的好处是能够随时兼容新设备)。
例如:去年发布的第一代 iPhone X 对应的 device mode 为 iPhone10,3 和 iPhone10,6,而今年最新发布 iPhone XS 对应 iPhone11,2,iPhone XS Max 对应 iPhone11,4 和 iPhone11,6,iPhone XR 对应 iPhone11,8,完整的 device mode 数据参考 Wiki:www.theiphonewiki.com/wiki/Models
综上,咱们能够先获取 device model 值,记为 platform,而后进行对比判断,转换成具体的设备型号,实现代码以下图所示。
备注:图中代码只给了对 iPhone 设备型号的判断,而完整的包括 iPad 和 iPod touch 型号我已经放在 GitHub Gist 上,你们能够参考,详见这里:gist.github.com/kangzubin/5…
参考连接:
**做者:**这个汤圆没有馅
在 Masonary 中也能够设置约束的优先级,如make.left.equalTo(weakSelf.view.mas_left).offset(20).priority(250)
中的 priority。
在 Storyboard 中也能够,举个🌰:父视图上有 imgView 和两个 label,现要求两个 label 的宽度随内容且不超出,另必须保证红色 label 中的内容显示完整。以下图。
storyboard 拖控件就不说了,直接从约束开始。
imgView: left、right、top、height、width 绿色label:left、center-y、right、height 红色label:left、center-y、right、height
这个时候 storyboard 会报错,由于两个 label 的宽度没法定位。以下图。
提示说,下降红色 label 的水平方向压缩阻力(即容易被压缩)以确保在其余视图以前能够被裁剪。点击 Change Priority,改变约束优先级。
如上图,咱们能够看 Size Inspector 中,红色 label 水平方向压缩阻力由750降为了749,说明在水平方向上,绿色 label 展现的优先级要高于红色 label。固然这和咱们一开始的需求反了,待会儿再改。咱们先看看 Size Inspector 中的说明。
Priority 的值默认分为三个等级 Required(1000)、High(750)、Low(250),其实能够输入任意其余数字。
好,回到需求,只要把红色 label 的水平方向压缩阻力优先级的值改为任意大于绿色 label压缩阻力的值便可。若是红色 label 的内容太多,那就会把绿色 label 给挤没掉。以下图。
**做者:**陈满iOS
一、相同 windowLevel
下,调整 UIWindow 显示层的基本方法
customWindow.hidden = NO;
复制代码
虽然设置本身的 hidden 便可显示出来,但上述方法并不会"自动"影响以前显示的 UIWindow 对象的 hidden 属性。若是,以前 UIWindow 的
hidden = NO
,设置新 UIWindow 的 hidden 将旧 UIWindow 覆盖后,旧 UIWindow 的 hidden 属性依旧为 NO。
customWindow.hidden = YES;
复制代码
若是你没有专门设置过 hidden 属性,系统默认为 YES。上述代码会将 UIWindow 绝对隐藏,无论有没其余 UIWindow 覆盖。当也没有其它非隐藏的 UIWindow 的时候,APP 屏幕彻底黑屏。
- (void)makeKeyAndVisible
复制代码
上述方法真的会将其显示在同一windowLevel的其它任何UIWindow之上!显示最上层的UIWindow以最后执行过该代码的UIWindow为准。
[self.window makeKeyAndVisible];
复制代码
其执行效果包括 但不限于 执行了以下代码(由于还会覆盖同 level 的全部 window):
[self.window makeKeyWindow];
self.window.hidden = NO;
复制代码
讲真,makeKeyAndVisible
真的会自动改变 hidden 属性值为 NO。
若是你仅仅建立一个 UIWindow,而又不专门设置 hidden 属性(或者makeKeyAndVisible),系统默认分配的默认值为true。
keyWindow
的混淆易错点设置 keyWindow 与否并不影响视图层级显示,仅来接收键盘及其它非触摸事件。若是没有专门设置过 keyWindow 的 hiden 为 NO,并且也没有其它非隐藏的 UIWindow,那么APP会黑屏。
- (void)makeKeyWindow
复制代码
- (void)resignKeyWindow
复制代码
app 的 keyWindow 与是否在最上层显示没有任何关系。好比,你若是想经过 [[UIApplication sharedApplication] keyWindow]
获取正在显示的 UIWindow 是极其不许确 的。有时候经过这个代码获取的若是真的是正在显示的 UIWindow,仅仅是由于碰巧而已。
hidden
属性 =NO
的 UIWindow
,该显示谁?如上所见,makeKeyAndVisible
与 hidden 的 setter 方法都可以改变 hidden 的值,但有个问题,通过屡次调整,可能有多个 UIWindow 的 hidden 都为 NO,那么应该显示谁?
.hidden=NO
的 UIWindow 为准,且执行 .hidden=NO 以前 hidden 的值为 YES。(hidden若是是从NO改成NO的不 算 最后 改变UIWindow的显示状态)二、基于 windowLevel
,调整 UIWindow 显示层的拓展方法
先去 UIWindow.h 里面看看 UIWindowLevel 的定义:
例如,在手势相关类中调整自定义的 UIWindow 层级
[self.window makeKeyAndVisible];
_window.windowLevel = UIWindowLevelAlert;
复制代码
(lldb) po self.window.windowLevel
2000
复制代码
(lldb) po self.window.windowLevel
1000
复制代码
(lldb) po self.window.windowLevel
0
复制代码
小结:
一、获取 App 全部 window 的 windows 数组
[[UIApplication sharedApplication] windows]
复制代码
例如,第三方加载动画框架 KVNProcess 中 KVNProgress.m 文件会有一段这样的代码,以下图1所示
二、keyWindow
[[UIApplication sharedApplication] keyWindow]
复制代码
例如,第三方下拉菜单框架 FFDropDownMenu 的 FFDropDownMenuView.m 文件中有这样一段代码:
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
[keyWindow addSubview:self];
复制代码
这段代码的目的是添加到最上层 UIWindow,但实际操做是把本身的视图添加到 keyWindow 上。其实,若是咱们在编写代码时严谨地保证 keyWindow 是显示在最上层的 UIWindow,这样写没有问题。但若是:本身或者其它第三方框架曾经调高过其它 UIWindow 属性 windowLevel,或者有同级 windowLevel 的其它 UIWindow 后来改变过显示状态(如 .hidden=NO,makeKeyAndVisible 等),可能会致使下拉菜单的弹出视图没法显示(被覆盖)。
三、获取 AppDelegate 单例的 window 属性 专门获取 AppDelegate.m 文件中的 window 属性,不包含其它其定义的 window
[[[UIApplication sharedApplication] delegate] window]
复制代码
欢迎关注咱们的公众号:iOS-Tips,也欢迎加入咱们的群组讨论问题。能够公众号留言 ios
、flutter
等关键词获取入群方式。
推荐阅读