又到了咱们每个月一次的活动日了。今天咱们仍是抽奖,送出 10 枚 MWeb for Mac 的兑换码。MWeb 是一款专业的 Markdown 写做、记笔记、静态博客生成软件,功能至关强大,相信用过的童鞋都知道。做者 oulvhai
,目前是独立开发者。这里感谢 oulvhai 为咱们提供的兑换码。html
MWeb 相关的介绍你们能够查看官网,咱们此次的兑换码是非 Mac App Store 版,还请中奖的小伙伴在官网下载试用版,而后激活便可。ios
关注公众号在文章末尾抽奖web
上周公众号发布的如下文章:数组
本期知识小集的主要内容包括:bash
做者: halohilyapp
在项目中看到一段使用 GCD group 处理的代码,简化下来大概以下图异步
dispatch_group_notify
的调用放在了 dispatch_group_async
的 block 中,乍一看会有是否产生永久阻塞的疑问,由于子任务完成后的派发任务被放在了一个子任务中。然而其实这是不会阻塞的,代码会按编写人的预期进行执行,即 log1 输出以后,输出 log2。这是由于 dispatch_group_notify 的 block 是异步执行的。async
再举个例子,以下图,执行结果依次会是:log 1,log 2 ,log 4 ,log 5 ,log 3。工具
虽然此处结果正确,但这种将 dispatch_group_notify 的调用放在某一个子任务的执行块中的写法是不被推荐的,它不但反逻辑,并且并不总能保证结果正确。好比若是在此例中,在调用了 dispatch_group_notify 的子任务以后,又为该任务组使用 dispatch_group_async 语句添加后续子任务,这时代码的执行结果是不肯定的。测试
既然最开始的例子中执行结果是正确的,有的同窗会问,若是把 dispatch_group_notify 的调用放在全部子任务的最前面,以下图,是否也能得到预期的结果呢?答案是否认的,由于在最开始调用 dispatch_group_notify 时,子任务数量为0,它的代码块会当即执行。然后为该组派发了多个子任务,当这些子任务都执行完毕后,也并不会再次触发 dispatch_group_notify 的代码块。
做者: Lefe_x
最近项目中遇到一个内存泄漏的问题,SecondViewController 这个类在 pop 后并无执行 dealloc 方法,也就没有被正常被释放。使用内存泄漏工具排查,并无发现有循环引用的地方,手动查了一下也没发现异常。正在迷茫的时候,忽然看到了一个注册监听的地方。实现方式相似下面这样:
- (void)dealloc {
[[Manager sharedInstance] removeObserver:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[[Manager sharedInstance] addObserver:self];
}
复制代码
看到这里你应该已经猜到 SecondViewController 为何没被释放,它被 Manager 持有了,而 Manager 是一个单例,天然 SecondViewController 也不会被释放,dealloc 方法也不会执行。
这种设计很常见,每每给某个服务注册监听,达到相似通知的效果。若是使用数组保存监听者,监听者将会被数组持有。有同窗可能说,能够在 viewDidAppear 注册,在 viewWillDisappear 移除,这样 SecondViewController 就会被释放。可是,这样设计很糟糕,咱们尽可能不去约束调用者如何调用某个 API。
其实正确的作法是使用一个弱引用容器,咱们可使用 NSHashTable 来保存监听者,这样当监听者释放后,将自动从 NSHashTable 中移除,也不须要主动调用移除监听者的方法(也能够调用,视状况而定)。下面是一个简单的实现,你也能够参考 YYTextKeyboardManager 的实现:
_listenerTable = [NSHashTable weakObjectsHashTable];
- (void)addObserver:(NSObject *)obj {
[self.listenerTable addObject:obj];
}
- (void)removeObserver:(NSObject *)obj {
[self.listenerTable removeObject:obj];
}
复制代码
做者: Vong_HUST
相信 UIAlertView
你们应该都很熟悉,可是最近遇到一个坑。
因为历史缘由,项目中还在大量使用 UIAlertView。某天测试过来反馈说,评论框字符长度超过最大长度时,点击发送,弹出一个 alert
提示,点击肯定后,评论框没法在被激活,也就是无法弹出键盘了。非常怪异,debug
无果,搜了一下 stackoverflow
,发现有人遇到过相似的问题,能够点击末尾的参考连接来查看具体详情。
他给出的解决方案就是把这种状况下的 UIAlertView 换成 UIAlertController。试了下这种方式,果真是可行的,因为以前 UIAlertView 是不依赖其它视图层级的,建立后直接 show
就能够了,因此不少地方直接写在了非视图控制器类中。在换成 UIAlertController 以后,因为它是继承自 UIViewController 的,因此必需要有 VC 把它 present
起来。解决方案也很简单,写一个 UIViewController 的分类获取当前顶部可见的 ViewController,而后在上面 present
出 UIAlertController 便可,获取顶部可见 ViewController 的代码随便一搜就能够找到,这边就不贴了。
PS:UIAlertController 是 iOS8 之后才提供的,不过相信你们也不用适配 iOS8 以前的系统了吧😂。若是还要适配,那就只能作版本区分了。。。
参考
iOS 9 - Keyboard pops up after UIAlertView dismissed
做者: 高老师很忙
iOS12提供了 Shortcuts
的功能,今天给你们介绍 2 个苹果提供的提升 Shortcuts 调试效率的小技巧。
iPhone设置
->开发者
里面提供了 Display Recent Shorts 和 Display Donations on Lock Screen 的开关,能够忽略系统当前的建议和预测,显示咱们须要调试的 Shortcuts;同时还支持 Force Sync Shortcuts to Watch 的功能,手动去强制同步到 Watch,会节省不少时间哦!我以为这2个小技巧仍是很实用的,若是有其它小技巧欢迎一块儿交流!
做者: KANGZUBIN
在平常开发中,咱们常常会在 Debug 模式下打断点进行调试,并经过 LLDB 的 po
命令在控制台打印一些变量的值,以方便排查问题。
今天在 Release 模式下编译运行项目,发现要打印某一变量的值时(po xxx
),报以下错误:
error: Couldn't materialize: couldn't get the value of variable xxx: no location, value may have been optimized out
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression 复制代码
大体意思是说,xxx
的值不存在,可能已经被编译优化了。并且在断点模式下当咱们把鼠标的箭头移到某一变量上要进行快速浏览时,发现它们的值都是 nil
。
查了一下才发现,原来这与 Xcode 工程的编译选项 Optimization Level
设置有关,它是指编译器的优化级别,优化后的代码效率比较高,可是可读性比较差,且编译时间更长,它有 6 个选项值以下图:
上述每选项值的详细说明能够参考《Xcode 中 Optimization Level 的设置》和《如何加快编译速度》两篇文章,咱们这里再也不赘述。
Xcode 工程的 Optimization Level 值在 Debug 模式下默认为 None [-O0]
,表示编译器不会尝试优化代码,保证调试时输出指望的结果;而在 Release 模式下默认为 Fastest, Smallest[-Os]
,表示编译器将执行全部优化,且不会增长代码的长度,它是可执行文件占用更少内存的首选方案。
这也是为何咱们在 Release 模式下断点打印变量会报错,由于编译器已经给代码作了优化,它将不在调试时记录变量的值了。
此外,有时候遇到一些线上 Bug 可是在 Debug 调试时却没法复现,我猜有可能会跟编译优化有关,你以为呢?欢迎留言讨论。
做者: 我是乔忘记疯狂
Watch OS 4 新的 API,用于解决手腕转动时手表屏幕可能自动休眠的问题。好比在 watch 上展现二维码给各类 POS 扫描,屏幕常常会自动休眠。经测支付宝 watch app 已经使用了此 API,不过官方推荐只针对某些特定界面打开此功能,而支付宝是整个 watch app 都打开了此功能。