「 iOS知识小集 」2018 · 第 35 期

栏目: IOS · 发布时间: 5年前

内容简介:作者:hite和落雁卡顿检测系统,用于检测 App 的主线程运行情况。在追求 N 个 9 奔溃之外,卡顿也是我们极其重要运行指标。很遗憾,世界上最好的免费 APM 平台 Fabric 却没有。而国内的 bugly、网易云捕等,都提供了类似的功能。如下图是云捕的卡顿功能。

作者:hite和落雁

卡顿检测系统,用于检测 App 的主线程运行情况。在追求 N 个 9 奔溃之外,卡顿也是我们极其重要运行指标。

很遗憾,世界上最好的免费 APM 平台 Fabric 却没有。而国内的 bugly、网易云捕等,都提供了类似的功能。如下图是云捕的卡顿功能。

「 iOS知识小集 」2018 · 第 35 期

说起来卡顿检测,技术原理很简单,下面是来自 bugly 的 QA 里的描述

iOS 卡顿检查的依据是监控主线程 Runloop 的执行,观察执行耗时是否超过预定阀值(默认阀值为3000ms) 在监控到卡顿时会立即记录线程堆栈到本地,在App从后台切换到前台时,执行上报。
复制代码

卡顿检测系统,这个大任务,可以分解为两部分:卡顿的检测 + 卡顿的展示和管理。

卡顿的收集

卡顿的收集,有现成的代码,核心代码可查看,gist gist.github.com/hite/1a7ee4…

检测到之后,需要获取当前时刻的堆栈,所有线程的堆栈(其实只需要主线程就够了)。

OK,拿来主义,一个很有名 PLCrashReporter 。我自己集成测试,在我的老古董机器,iPhone 6 跑,卡顿是能检测到,但是整个软件基本不可用,整个界面全卡住了。PLCrashReporter 生成日志代码如下图所示。

「 iOS知识小集 」2018 · 第 35 期

性能非常差,完全不可用。

拿不到堆栈信息,无法展示,所以只能采用造轮子的方式。根据戴铭 blog 里的例子,我改造了下,如 gist gist.github.com/hite/1a7ee4… 所示。我们收集到了所有堆栈的栈顶地址;接下来我们需要将这些栈信息符号化。

很容易想到的方案是传到自己的服务器上,用 Mac 环境处理堆栈的符号化,转换为可读的堆栈数据——代价太大,而且还不经济。

在浏览了 fabric 的各项 API 后,我发现有一个很讨好的接口,recordCustomExceptionName

符号化堆栈,卡顿管理

fabric 提供了 recordCustomExceptionName 接口,接口签名如下图所示。

「 iOS知识小集 」2018 · 第 35 期

我们利用这个接口,将第一步收集的堆栈数据传给 fabric,让 fabric 给我们符号化,而且 fabric 卡顿日志还能够聚合、分类、分组、跟踪。crash 日志的那一套都可以用上, fabric 用户对此是熟悉的。核心代码如下图所示。

「 iOS知识小集 」2018 · 第 35 期

至此,我们用很少的代价就做好了一个卡顿检测的系统,并且和奔溃功能一起使用,集中处理 APM 各项指标。

这个方案在月活几十万的 App 应用了快半年了。用户一点都没有感受到有卡顿系统,对现有系统影响很小。如下图所示。

「 iOS知识小集 」2018 · 第 35 期

所有的数据都在 crashlytics 里,选择 non-fatal,即可,从统计下来的数据来看,卡顿主要体验在;

  1. 读写文件
  2. 读写数据库
  3. 处理图片
  4. 动画渲染
  5. 在非主线程读一些主线程才能用的属性(奇怪吧?)

基本上和我们猜想是一致的,接下来就需要跟踪和处理这些卡顿。

目前这个系统有两个缺陷:

recordCustomExceptionName

关于定位的一个小知识点

作者:高老师很忙

今天分享一个轻松的小知识点~~~

搜索了网上关于 iOS 定位的文章,很多在 locationManager(_:didUpdateLocations:) 收到回调就执行了 stopUpdatingLocation() ,如下图:

「 iOS知识小集 」2018 · 第 35 期

然而在一些情况之下,这样写是有隐患的(如下图),

「 iOS知识小集 」2018 · 第 35 期

在某次运行的时候(并不是每次出现),在 21 点 16 分返回了一个 21 点 09 分的点,这是因为 CoreLocation 可能会返回一个缓存的值给我们,所以我们使用的时候应该判断一下时间戳(如下图),这样可以减少定位偏差。

「 iOS知识小集 」2018 · 第 35 期

参考链接:O网页链接

iOS 获取设备型号最新总结

作者:KANGZUBIN

在开发中,我们经常需要获取设备的型号(如 iPhone X,iPhone 8 Plus 等)以进行数据统计,或者做不同的适配。但苹果并没有提供相应的系统 API 让我们直接取得当前设备的型号。

其中,UIDevice 有一个属性 model 只是用于获取 iOS 设备的类型,如 iPhone,iPod touch,iPad 等;而其另一个属性 name 表示当前设备的名称,由用户在设置》通用》关于》名称中设定,如 My iPhone,xxx 的 iPhone 等。然而,我们无法根据这两个值获得具体的型号。

不过,每一种 iOS 设备型号都有对应的一个或多个硬件编码/标识符,称为 device model 或者叫 machine name,之前的小集介绍过,我们可以通过如下图中的代码来获取。

「 iOS知识小集 」2018 · 第 35 期

所以,通常的做法是,先获取设备的 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,然后进行对比判断,转换成具体的设备型号,实现代码如下图所示。

「 iOS知识小集 」2018 · 第 35 期
「 iOS知识小集 」2018 · 第 35 期

备注:图中代码只给了对 iPhone 设备型号的判断,而完整的包括 iPad 和 iPod touch 型号我已经放在 GitHub Gist 上,大家可以参考,详见这里: gist.github.com/kangzubin/5…

参考链接:

Storyboard 中的约束优先级

**作者:**这个汤圆没有馅

在 Masonary 中也可以设置约束的优先级,如 make.left.equalTo(weakSelf.view.mas_left).offset(20).priority(250) 中的 priority。

在 Storyboard 中也可以,举个:chestnut::父视图上有 imgView 和两个 label,现要求两个 label 的宽度随内容且不超出,另必须保证红色 label 中的内容显示完整。如下图。

「 iOS知识小集 」2018 · 第 35 期

storyboard 拖控件就不说了,直接从约束开始。

imgView: left、right、top、height、width 绿色label:left、center-y、right、height 红色label:left、center-y、right、height

这个时候 storyboard 会报错,因为两个 label 的宽度无法定位。如下图。

「 iOS知识小集 」2018 · 第 35 期

提示说,降低红色 label 的水平方向压缩阻力(即容易被压缩)以确保在其他视图之前可以被裁剪。点击 Change Priority,改变约束优先级。

「 iOS知识小集 」2018 · 第 35 期

如上图,我们可以看 Size Inspector 中,红色 label 水平方向压缩阻力由750降为了749,说明在水平方向上,绿色 label 展示的优先级要高于红色 label。当然这和我们一开始的需求反了,待会儿再改。我们先看看 Size Inspector 中的说明。

  • Content Hugging Priority :拉伸阻力,即抗拉伸。值越大,越不容易被拉伸。
  • Content Compression Resistance Priority :压缩阻力,即抗压缩。值越大,越不容易被压缩。
  • Intrinsic Size :控件未设置宽高约束时用的。
  • Ambiguity :解决冲突时是否需要验证。

Priority 的值默认分为三个等级 Required(1000)、High(750)、Low(250),其实可以输入任意其他数字。

好,回到需求,只要把红色 label 的水平方向压缩阻力优先级的值改成任意大于绿色 label压缩阻力的值即可。如果红色 label 的内容太多,那就会把绿色 label 给挤没掉。如下图。

「 iOS知识小集 」2018 · 第 35 期

UIWindow 的显示特性与常见操作方法小结

**作者:**陈满iOS

UIWindow 的显示特性

1、相同 windowLevel 下,调整 UIWindow 显示层的基本方法

  1. 显示相关属性:hidden
  • 如果仅仅想显示一个UIWindow
customWindow.hidden = NO;
复制代码

虽然设置自己的 hidden 即可显示出来,但上述方法并不会"自动"影响之前显示的 UIWindow 对象的 hidden 属性。如果,之前 UIWindow 的 hidden = NO ,设置新 UIWindow 的 hidden 将旧 UIWindow 覆盖后,旧 UIWindow 的 hidden 属性依旧为 NO。

  • 如果仅仅想隐藏一个 UIWindow
customWindow.hidden = YES;
复制代码

如果你没有专门设置过 hidden 属性,系统默认为 YES。上述代码会将 UIWindow 绝对隐藏,不管有没其他 UIWindow 覆盖。当也没有其它非隐藏的 UIWindow 的时候,APP 屏幕完全黑屏。

  • 如果想显示一个UIWindow,同时设置为keyWindow,并将其显示在同一windowLevel的其它任何UIWindow之上
- (void)makeKeyAndVisible
复制代码

上述方法真的会将其显示在同一windowLevel的其它任何UIWindow之上!显示最上层的UIWindow以最后执行过该代码的UIWindow为准。

  1. 显示相关方法:makeKeyAndVisible 的作用
[self.window makeKeyAndVisible];
复制代码

其执行效果包括 但不限于 执行了如下代码(因为还会覆盖同 level 的所有 window):

[self.window makeKeyWindow];
self.window.hidden = NO;
复制代码

讲真, makeKeyAndVisible 真的会自动改变 hidden 属性值为 NO。

  1. UIWindow 对象的 hidden 属性默认值
  • 默认值:true

如果你仅仅创建一个 UIWindow,而又不专门设置 hidden 属性(或者makeKeyAndVisible),系统默认分配的默认值为true。

  1. 误区:关于 keyWindow 的混淆易错点

设置 keyWindow 与否并不影响视图层级显示,仅来接收键盘及其它非触摸事件。如果没有专门设置过 keyWindow 的 hiden 为 NO,而且也没有其它非隐藏的 UIWindow,那么APP会黑屏。

  • 如果仅仅设置为keyWindow
- (void)makeKeyWindow
复制代码
  • 如果仅仅解除为keyWindow
- (void)resignKeyWindow
复制代码

app 的 keyWindow 与是否在最上层显示没有任何关系。比如,你如果想通过 [[UIApplication sharedApplication] keyWindow] 获取正在显示的 UIWindow 是极其不准确 的。有时候通过这个代码获取的如果真的是正在显示的 UIWindow,仅仅是因为碰巧而已。

  1. 警惕点:有多个 hidden 属性 =NOUIWindow ,该显示谁?

如上所见, makeKeyAndVisible 与 hidden 的 setter 方法均可以改变 hidden 的值,但有个问题,经过多次调整,可能有多个 UIWindow 的 hidden 都为 NO,那么应该显示谁?

.hidden=NO

2、基于 windowLevel ,调整 UIWindow 显示层的拓展方法

先去 UIWindow.h 里面看看 UIWindowLevel 的定义:

「 iOS知识小集 」2018 · 第 35 期

例如,在手势相关类中调整自定义的 UIWindow 层级

[self.window makeKeyAndVisible]; 
_window.windowLevel = UIWindowLevelAlert;
复制代码
  • 打印代表 UIWindowLevelAlert 层级的数据值
(lldb) po self.window.windowLevel
2000
复制代码
  • 同理,打印代表UIWindowLevelStatusBar层级的数据值
(lldb) po self.window.windowLevel
1000
复制代码
  • 同理,打印代表UIWindowLevelNormal层级的数据值
(lldb) po self.window.windowLevel
0
复制代码

小结:

  1. windowLevel 数值越大的显示在窗口栈的越上面
  2. 显示层的优先级 为: UIWindowLevelAlert > UIWindowLevelStatusBar > UIWindowLevelNormal
  3. 系统给 UIWindow 默认的 windowLevel 为UIWindowLevelNormal

UIWindow常见操作方法总结

1、获取 App 所有 window 的 windows 数组

[[UIApplication sharedApplication] windows]
复制代码

例如,第三方加载动画框架 KVNProcess 中 KVNProgress.m 文件会有一段这样的代码,如下图1所示

2、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 等),可能会导致下拉菜单的弹出视图无法显示(被覆盖)。

3、获取 AppDelegate 单例的 window 属性 专门获取 AppDelegate.m 文件中的 window 属性,不包含其它其定义的 window

[[[UIApplication sharedApplication] delegate] window]
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

REST实战

REST实战

Jim Webber、Savas Parastatidis、Ian Robinson / 李锟、俞黎敏、马钧、崔毅 / 东南大学出版社 / 2011-10 / 78.00元

为何典型的企业项目无法像你为web所开发的项目那样运行得如此平滑?对于建造分布式和企业级的应用来说,rest架构风格真的提供了一个可行的替代选择吗? 在这本富有洞察力的书中,三位soa专家对于rest进行了讲求实际的解释,并且通过将web的指导原理应用到普通的企业计算问题中,向你展示了如何开发简单的、优雅的分布式超媒体系统。你将会学习到很多技术,并且随着一家典型的公司从最初的小企业逐渐成长为......一起来看看 《REST实战》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具