支持 Xcode 10.1 或以下版本在 iOS 12.3 上进行真机测试的解决方案

问题的发生​

Apple 在北京时间 5 月 14 日凌晨发布了 iOS 12.3 等系统更新。

一方面,iOS 12.3 更新优化了系统整体流畅性,进行了一系列功能改进[1],最重要的——带回了体验更流畅、观感更舒适的后台任务动画。这些原因让我对这次更新非常期待,阶段性结束了一系列产品需求后,我就把自己的主力机 iPhone XS 升级到了 iOS 12.3。

另一方面,Xcode 在上一个更新周期 (即 Xcode 10.2) 中就不再支持 Swift 3.x。目前我工作中接触的工程绝大多数采用 Swift 3.2 和 Objective-C 混编,少部分升级到了 Swift 4.0,还有极少部分是纯 Objective-C 工程。上一个更新周期,我提取了 Xcode 10.2 中关于 iOS 12.2 的 device support 文件放入老版本的 Xcode 中,算是解决了 IDE 和设备系统不兼容的问题。

这次,在 Xcode 版本没有更新的情况下,为了能够在 iOS 12.3 系统的设备上做真机测试,Xcode 的做法是在 10.2.1 版本上在 IDE 层级上与 iOS 12.2 共用同一套 device support 文件,但是这种方法在 Xcode 10.1 上是失效的。​

问题的解决​

错误的尝试:

将 iOS 12.2 的 device support 复制一份并重命名为”12.3(16F156)”,真机测试后发现并没有生效。

有效的方案:

修改 device support 中的数字签名,重命名文件夹为”12.3(16F156)”。

修改后的文件的下载链接: https://zhr.moe/wp-content/uploads/2019/05/12.3-16F156.zip

使用方法:解压后将文件夹放入 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport ,并重新启动 Xcode。

 

[1] 关于 iOS 12.3 的功能改进参考: https://applech2.com/archives/20190514-apple-ios-12-3-now-available.html ; 关于 iOS 12.3 的安全性内容参考: https://support.apple.com/zh-cn/HT210118

2018-19 NBA 季后赛首轮球评集锦

本文同时发表在简书《2018-19 NBA 季后赛首轮球评集锦》和 CYaRon! Blog《2018-19 NBA 季后赛首轮球评集锦

4.15 凯尔特人 1-0 步行者

在这个年代,你可能很难看到一场一方只得 74 分的 NBA 比赛了,如果不算垃圾时间,步行者在比赛的前 45 分钟里,仅得到了 64 分。但是,这种比分很可能是凯尔特人与步行者系列赛的主旋律。

上半场,步行者包夹欧文看上去效果不错,但是莫里斯总是能见缝插针获得上罚球线的机会。这段文字看上去平淡无奇,背后隐藏的却是欧文在包夹情况下的快速出球和步行者对侧翼防守的不足。

下半场,凯尔特人的表演开始了。实际上,造成步行者第三节只有 8 分进账的一大主要原因,还是他们在第三节前半段没有任何进攻手感。凯尔特人越来越放心地在外线放空除了博格丹诺维奇外的所有步行者球员,然后掐断迈尔斯特纳进入内线的路径,造成步行者在第三节前 8 分钟里只得 1 分的尴尬状况。同时,由于进攻发起通常是失误转换或者后场篮板,凯尔特人的上球速度在欧文的主导下比步行者的节奏快得多,步行者的防守应接不暇接连出错。

特别地,作为季后赛第一场,我还要特别提出欧文和海沃德两人与过去的不同:

对欧文来说,上赛季的他改变了防守端的很多习惯打法,而这赛季就是他收获果实的时候。而海沃德经历一个赛季的漫长调整,如今他已经不再畏惧身体对抗,放开了身上的包袱,也重新适应起了自己伤愈恢复后的脚踝。

凯尔特人的改进空间主要还是围绕着几个问题:

  1. 第二阵容上场后的节奏和第一阵容有明显的区别,这种断层是步行者在第二节获得领先的原因。
  2. 在霍福德和贝恩斯双内线的阵容下,凯尔特人是否具有继续提速的空间,毕竟不是每轮季后赛对手的大中锋都只会一手背筐且从不飘在外线的。
  3. 对如此天赋级别的凯尔特人,依然要强调进攻节奏和进攻效率,本场比赛凯尔特人一共只打了 77 个回合得到 84 分,如何把比赛带入对攻战和如何打好对攻战,也是凯尔特人需要修炼的课题。

4.16 勇士 1-1 快船

考辛斯的伤病,可以让勇士放心大胆地使用更适合勇士既有体系的博古特,而不用担心浪费考辛斯的天赋。

哦对了,似乎鹈鹕拥有考辛斯和戴维斯双塔的时候,面对考辛斯的伤病,我也对鹈鹕有过类似的建议。失去了考辛斯的鹈鹕从季后赛边缘球队升级成了首轮横扫开拓者,次轮才输给勇士的球队。

4.17 掘金 1-1 马刺

马刺最惧怕的还是绝对的天赋——无论是身体上的还是技术上的。如果有那么一位球员,能抗着整支球队神经刀一样的冲锋,那就是马刺真正的梦魇。

于是在近些年,我们见到 2012 年韦少带领的雷霆, 2013 年勒布朗的热火……马刺不能阻挡他们,无论波波维奇排出什么样的阵容,马刺打什么样的战术,都不行。

马刺今天的梦魇,就是前三节 8 中 0 ,第四节 9 中 8 的默里。这组对决的 matchup 当中,本来马刺是对得上掘金锋线上的每一名球员的,但是今天:

  1. 德罗赞进攻火力全开,防守端注意力不集中
  2. 裁判不太给马刺面子(仅下半场)
  3. 马刺的第四节进攻秩序及其混乱(阿尔德里奇和怀特居然没有运动战出手)

于是结果就是马刺在客场被逆转。但即使如此,我还是坚持相信马刺会取得这轮系列赛的胜利:

  1. 马刺以 7 号种子身份拼下了一场客场,之后的两场比赛马刺将回到主场
  2. 掘金并不是每一场都会出现今天第四节的默里,马刺依然可以用整体性化解掘金的招式
  3. 掘金真正的对位优势是约基奇,但是约老师还不具备保持 36 分钟高效输出的能力,或者说,掘金还没有意识到他们应该什么时候主打约基奇这个点
  4. 马刺在锋线上的对位是碾压掘金的

至少系列赛到这个时候,我还是愿意相信马刺拥有更高的胜率。

4.18 凯尔特人 2-0 步行者

这两个队很有趣,只要进攻端开始断电,就要断个痛快。比如,第三节步行者送凯尔特人一波 8-0,反过来凯尔特人又在第四节送步行者一波 16-0。

步行者似乎是找到了解决凯尔特人突击问题的解决方案,他们主动让迈尔斯特纳换到凯尔特人的突击点,然后利用特纳的封盖和干扰降低突进的成功率。

凯尔特人目前对这招还真没什么解法,第四节的一波流也来自于欧文的外线远投。对于凯尔特人来说,他们还是需要继续做好防守打转换,以及把那些该死的空位投篮投进去。


今天赢下比赛之后,欧文回到更衣室的路上,第一个拥抱的是一个穿着褐色夹克的比他矮半头的人——朱利安埃德尔曼。

这不仅意味着我最欣赏的篮球控球后卫拥抱了我最欣赏的橄榄球外接手,同时,这是一座城市的体育氛围最好的体现。

4.19 掘金 1-2 马刺

马刺和掘金系列赛的整体走向终于向着我曾经设想的方向发展了。

今天掘金的调整:

  1. 加重约基奇和米尔萨普在进攻端的戏份。掘金设想的状况是,马刺永远只有阿尔德里奇一名内线球员可以对上约基奇,而且阿德的护框能力相对一般。如果约基奇可以打开局面,马刺就必须把阿德放在场上,这时让锋线球员参与到进攻中,阿德将很难在防守端找到自己的位置。但是,正如我在这组系列赛上一场比赛之后判断的那样,约基奇很难满功率输出 36 分钟,掘金应对这种状况准备的后招就是米尔萨普。必须说明,这种打法掘金很少去用,效果也是很明显的不太好,关于米尔萨普后面还会讨论到。
  2. 把哈里斯调整成突击手,默里调整成射手。这是第一条调整的衍生品,本意上是先用约基奇把局面打开,然后这条锋线开始参与进攻,打乱马刺的防守重心。这里面有一个问题很难解决,掘金的这条锋线在某种程度上非常依赖米尔萨普这条天然屏障。在第一条里面我们提到,米尔萨普已经成为配合约基奇打内线威胁的帮手,这就要求他的位置一直都很深,自然就切断了米尔萨普和整条锋线的联系。
  3. (我个人最不解的一点)防守端包夹阿尔德里奇。阿德是一个中投靓仔技能包加持的组织前锋,如果你只看到阿德的中投能力而忘记了他的组织属性,就会发生这种包夹阿德之后被打一波流的情况(第四节初)。很明显,当阿德被包夹之后,他的快速出球可以帮助弱侧形成 4 打 3 的状况,今天的德里克怀特又准得没道理,后果很严重啊。

当然,这一切的最基础的原因还是掘金因为发现自己锋线对位的劣势直接放弃了整条锋线,从而让自己的锋线看起来更加的无足轻重。

和马刺打球,最重要的就是整体性,当然,还得有一个疯起来就挡不住的“神经刀”球员才好啊。

4.21 掘金 2-2 马刺

马龙教练在尝试锋线硬碰硬和主打约基奇之后,设计了一个全新的克制马刺的思路。这个调整只动了一个点——把克莱格提上首发换下巴顿(同时调整他们的轮换时间比重),却达到了“一石三鸟”的效果。

  1. 压制德罗赞。掘金是明白的, G3 的小白和 G2 的默里一样,这并不是他们的常规状态,马刺的进攻大旗还是靠德罗赞撑起。德罗赞第四节那次发泄式的技术犯规已经展现出,本场比赛掘金对德罗赞的“特殊照顾”是很有效果的:克莱格对德罗赞的单防、整体更机动的防线切断德罗赞和队友的联系、哈里斯更多的去单防怀特……
  2. 造一个马刺惧怕的“进攻天赋”。之前提到过的,默里本人的状态延续不稳定,约基奇的持续输出不够可怕,那么掘金索性调整出一个克莱格,给他球权让他打。事实上,前三场比赛里,克莱格的进攻比巴顿更果断,也更适合对抗马刺这条机动性欠佳的防线。
  3. 欺负马刺的轮换深度。这是这个系列赛前马刺潜在的劣势,但从今天开始,这将成为一个人尽皆知的问题。掘金敢于用克莱格换下巴顿,但是马刺却没有阵容调整的空间。他们很难加重某个球员的角色(能打的球员使用率都压榨到了极限),也很难减少某个球员的戏份。波波维奇的所有调整几乎都必须做在场上,而不是某场比赛突然排出一个吓你一跳的新首发收获奇效。

这场比赛对马刺来说也发生了一些比较超出想象的情况:克莱格准得离谱(71.4%的三分球命中率)、裁判的判罚充满争议等等,但是马刺的调整没有跟上掘金也是事实。

我想,马刺在这轮系列赛的胜利之匙,依然是德里克怀特。

4.23 太阳主教练下课

太阳这个队的重建,有点像拖延症患者。

摆烂赛季打完,选中德文布克,换了个教练:“注意啦,我们要开始赢球了!”

打着打着,发现布克还是需要帮手,又回到摆烂状态,选中约什杰克逊,再次换了个教练:“去年遇到点意外,今年我们要开始赢球了!”

新赛季开始了,他们发现泰森钱德勒年龄大了,撑不住内线压力了,只好再次摆烂选中德安德烈艾顿,并且又换了个教练:“你看,我们终于凑齐了最后一块拼图,这下我们要开始赢球了!”

结果他们今年 19 胜 63 负。

而且他们又换了个教练。

4.26 掘金 3-3 马刺

波波维奇拼急了。

调整总体只有一条:直接弃用贝尔坦斯(马刺今天是 8 人轮换),打阿尔德里奇和珀尔特尔的高度优势,强行拉出空间。对标约基奇的内线冲击,马刺可以用内线高度也可以用外线炮台(虽然米尔斯的三分依然没眼看)。

除此之外,波波维奇应该是给球员灌过鸡汤的。在悬崖边上的比赛,尤其是对手基本把你琢磨透了的比赛,赢比赛的关键基本不是战术而是决心了。

4.26 NFL 选秀第一日(首轮)

爱国者在首轮 32 顺位如愿选到了天赋出众的进攻武器,亚利桑那州立大学的 WR N’Keal Harry。

选秀前,我对首轮选择的预测是 WR AJ Brown,总体来说,选择一个 WR 是今年选秀首轮普遍选择防守组球员的大环境下的最优解。

这次选中 Harry,其实和预想中选中 AB 的效果是类似的:加强 WR 位置上的对抗能力。在此之前的几年里,爱国者惯用的 WR 都是技巧能力强,对抗不占优的小外接,比如 Edelman/Amendora/Hogan 等等。敢于做这种选择,主要还是仰仗 Gronk 在 TE 位置上强大的单挑能力、吸引防守的能力以及打深远路线的能力;以及近几年在 NFL 整体风格偏向传球的大前提下,爱国者反而选择更多的冲跑地面进攻(去年首轮 31 位选下 RB 位置上的 Michel 就是最好的证明),试图掩盖外接身材不匹配的问题。

如今, Gronk 已经退役,爱国者的进攻端会遇到很大的挑战,为 Brady 补充一个可靠的进攻武器非常关键。Harry 的长处就是他有可靠的接球技术,以及持球跑路线的能力。在 Brady 的年龄问题逐渐开始影响他的出球质量的情况下,补充天赋就是雪中送炭。

预测:明天的爱国者,要集中选防守组球员了,尤其是 CB 位置。

4.27 NFL 选秀第二日(次轮、三轮)

爱国者用 56 号签和 101 号签和公羊向上交易到 45 号签选中 CB Joejuan Williams。

说起来,我昨天预测是要选防守组成员(尤其是 CB),今天也确实是选了一个 CB 一个 DT,但是我确实没看懂这么高位置选个 CB 的实际意义,尤其是家里还有 Gilmore 正在巅峰。

我强行扯个解释,选中 Williams 是爱国者为了应对越来越多的大个子脚步型 WR,需要选个具有绝对高度的 CB 对位吧(我觉得这个解释不行)。

4.28 掘金 4-3 马刺

在马龙教练那次极致的调整之前,马刺在队伍整体上还领先着掘金半个身位。但在那之后,尤其是我写了马龙教练对克莱格调整的球评之后,我才觉得掘金在系列赛里是多么高效地吸取着养分,获得了多么大的进步。

次轮,他们面对的开拓者,将是他们又一个磨炼后卫线和锋线实力的好对手。但是,如果掘金想要痛痛快快地把球赢下来,那么就必须调动起约基奇。说起来,雷霆打不到开拓者的痛处,可能是亚当斯对坎特还没有绝对的压制力,但掘金的内线冲击将是另一个等级的。

期待掘金的继续成长,他们一定能成长为一个合格的二号种子的模样。

聊聊 App Transport Security 和 HTTPS

App Transport Security

苹果总是成为引领行业的企业,即使很多产品和功能别人早就做出来了,苹果也能靠自己惊人的庞大体量和宣传效果让它火起来。去年,苹果在 iOS 9 和 OS X El Capitan 中引入了 ATS,顾名思义,这项功能屏蔽了所有 HTTP 访问,并保护用户的 HTTPS 访问。

由于这只是 app 中一项起到保护作用的功能,多数开发者一般选择关闭这项功能(下文中会提到)。可能是苹果的责任心爆发,他们要求开发者在 2017 年 1 月 1 日后提交的APP中必须启用 ATS,换句话说,以后的 iOS 应用必须采用 HTTPS 访问,不仅是数据抓取,即使是 web view 访问也必须是 HTTPS 的。

HTTPS

请写出专业名词全称(2 分):

HTTP: Hypertext Transfer Protocol

HTTPS: Hypertext Transfer Protocol Secure

SSL: Secure Sockets Layer

TLS: Transport Layer Security

CA: Certificate Authority

把上面的这些名词串在一起就可以解释 HTTPS 了。它是在 HTTP 的基础上进行加密的协议,而 SSL/TLS (在这里就不解释这二者了,它们可以简单地看做同一个东西在不同阶段的名字)在传输层上完成的就是加密的任务。

HTTPS 的工作原理广义上类似于人的社交关系。用火车站检票举例,火车站信任检票员可以胜任自己的工作,他们负责检查你的票和身份证是否和本人相符。如果你提供的信息是正确的,那么你将借由检票员获得火车站的信任,并允许你乘车。

回到 HTTPS,你信任你的浏览器通过可信的证书提供商 CA 进行验证,浏览器检查网站证书与网站内容是否相符,通过这层验证表示你通过你的浏览器信任了这个 HTTPS 网站。

在这之前可以耍的小聪明

ATS 强制使用 HTTPS,对于遍地 HTTP 的中国互联网环境来说,是个坏消息。虽然 HTTP 和 HTTPS 在迁移上并不复杂,但是一个好用的且可用于商业用途的HTTPS 证书价格不菲,这导致开发者一般都选择屏蔽这个功能。

在 info.plist 文件中,新建一个键为 NSAppTransportSecurity,在其中新建 Boolean 型的键 NSAllowsArbitraryLoads 设置为 YES。

访问 HTTP 网页报错

为了模拟这种情况,创建一个新工程,并添加了一个 webView,加入如下代码:

运行后,不会加载 QQ 的主页,同时会收到访问被禁止的 log,这表示 ATS 完全屏蔽了 HTTP 的访问。

访问 HTTPS 网页报错(-9802)

将上述的代码域名改为 https://www.baidu.com/ 后,得到了报错。

为什么我们访问了HTTPS协议的网站却依然报错呢?这里我们必须参考 NSAppTransportSecurity 的官方文档查找答案。

根据文档,ATS 不仅要求请求必须是 HTTPS 协议的,还有其他的 3 点要求:

1.TLS 协议的版本必须在 TLS1.2 以上

2.要求支持完全正向保密(Perfect Forward Privacy)的加密方式,列举如下:

  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

3.签名算法至少使用一个 SHA256 指纹和一个高于 2048 位的 RSA(Rivest-Shamir-Adleman) 或高于 256 位的 ECC(Elliptic-Curve Cryptography) 秘钥

检查百度的证书,发现它没有符合上述的第二个条件,引起了 ATS 的报错。

AFNetWorking + HTTPS(-1012)

作为 iOS/OS X 平台上最好的网络访问库,AFNetWorking 本身是支持 HTTPS 访问的,而且对于 HTTPS 的要求在某些方面甚至高于 ATS 本身。

由于私人开发者一般不愿意购买权威 CA 颁发的证书,所以 AFNetWorking 会判断我们的证书是非法的,这时我们有两种方法选择。

1. 让 AFNetWorking 完全不检测证书

早期的 AFNetWorking 中,我们可以通过引用的 pch(Precompile prefix header),设置一下 AFNetWorking 的配置:

后来,这种宏的设定失效了,于是我们可以修改库的安全政策:

另外,由于域名审核也是一个非常麻烦的过程,有的开发者还没有自己的域名,这时还需要设置不匹配域名:

2. 让 AFNetworking 检测本地提供的个人证书

这种方法则是把自己当做一个 CA,自己一定是信任自己的,由此检查访问的域名是否匹配。

由于库要求检查 cer 文件,如果你手中的证书是 pem 文件则可以进行转换:

然后设置安全策略:

AFNetworking 自动检查 bundle 当中的证书,这种方式的安全性相比第一种高了一些。

定制 ATS

这里列举的一些方式可能会在未来必须采用ATS后失效,但在目前是可用的。

1. 设置不使用 ATS 的域

在 info.plist 的 ATS 部分中的 NSExceptionDomains 中添加域名,并在其中将 NSIncludesSubdomains 和 NSExceptionsAllowsInsecureHTTPLoads 均设为 YES。

2. 设置使用 ATS 的域

首先按照上面的方法将 NSAllowsArbitraryLoads 设为 YES,并在 NSExceptionDomains 中添加域名,把 NSExceptionsAllowsInsecureHTTPLoads 置为 NO。

3. 降低 ATS 要求

同样在 NSExceptionDomains 中添加域名。如果需要降低 TLS 版本则修改 NSExceptionMinimumTLSVersion 为 TLSv1.1 或 TLSv1.0。如果不支持正向保密,则设置NSExceptionRequiresForwardSecrecy 为 NO。

小结

ATS 作为苹果保证用户安全地访问提供的一项功能,填补了在移动端用户不能了解自己是否访问了加密的站点的盲区。对这一功能的强行要求,可能会加强开发者甚至是用户对网络安全的重视。但即使如此,开发者也不能对安全问题掉以轻心,反而在关键服务上应该更加小心。

iOS 9.3 以上系统版本的迷之卡顿

作为一名SIF玩家,在不适当的时候弹出一条推送或者收到一个电话绝对有让人摔手机的冲动。但是自从升级到 iOS 9.3 以后,SIF 又出了个新的毛病——迷之卡顿。

不仅仅是日服客户端,盛大版、美服都遇到了这个问题。在不同版本出现了同一问题,我把问题归结为iOS在相关接口和实现上的改动。但是,这种情况的出现没有任何规律可循,不知道什么时候就会卡顿,而不是一直都在卡。(事实上,查阅 iOS 9.3 的 API Differences ,并没有发现 API 实现上的更新,苹果一般也不会在这样的版本里大幅度调整 API)

直到我在城际上玩 SIF,出站的时候突然发生了卡顿,随后屏幕跟着周围光线变得更亮,我开始怀疑这种卡顿的出现和屏幕亮度的自动调节相关。单一变量测试,关闭自动调节屏幕亮度之后,很长一段时间内没有出现卡顿。另外,即使是在不是游戏这种大量消耗资源的应用里,亮度的自动调节也会带来掉帧,这样一来几乎可以确认这个问题的源头了。

只不过,我们并不了解 iOS 9.3 后对屏幕亮度的调节具体做了哪些改变,我只能猜测是它使用了不该使用的资源,如果你对手机的帧率有非常高的要求并可以牺牲亮度调节功能的话(比如玩游戏),可以选择暂时关闭这个功能。

P.S. 关于自动调节亮度和 Night Shift 的一点解释

iOS 9.3 引入了 Night Shift 功能,即在夜间开启防蓝光模式,也就是俗话说的暖屏模式。理论上,降低蓝光有两种解决方案:

  1. 降低亮度
  2. 减弱蓝光

苹果用户应该比较了解,苹果的设备在最低亮度下依然保持着相当高的亮度,这是由屏幕的特性导致的。由此,苹果决定采用第二种方式,让屏幕变暖来解决问题。

你可能会问,是不是屏幕同时要照顾到亮度调节以及变暖才导致了卡顿呢?我认为理论上不是这样的。亮度调节是由屏幕背光板来完成的,而减弱蓝光则是完全让屏幕显示的颜色在 RGB 分布上向 R 倾斜,是由两个机制操作完成的,理论上不会出现互相占用资源的情况。

后续情况

非常不幸的是,6 月 6 日发布的最新测试版 iOS 9.3.3 beta2 中,自动调节亮度依然有掉帧的问题。好在 iOS 10 的预览版就要更新了,我们也只能期待这个问题尽快被解决了。

无法识别的 Selector 发送给实例的快速排错法

How to solve

Unrecognized selector sent to instance… 应该是 iOS 开发中比较常见的一种问题,但是一般这种错误的报错会指向 main.m,一时间很难找到是哪个对象发生了问题。这时,我们可以下一个 Debug 断点。

在 Xcode 的菜单栏中选择 Debug -> Breakpoints -> Create Symbolic Breakpoint… ,在弹出的标签的 Symbol 栏填入

这时再次运行就会发现真正出现问题的地方了。

Example

今天在给 WebView 的一个属性赋值的时候一直报错。如果初始化 WebView 时不涉及这个属性的话,则不会出现问题。虽然我已经定位了问题的所在,但是如何解决依然没有头绪。添加断点后立刻发现,给这个属性传入的对象的某个属性出现了循环引用,改成 weak 后解决了问题。

约瑟夫环问题的模拟和递归方法

Description

成都信息工程学院又要发年终大奖了!这次获大奖的人从n名会员中按以下规则选出:首先,让会员们围成一个大圈,按 0, 1, 2, …, n – 1编号.然后,随机抽取一个数 m,让编号为 0 的会员开始报数.每次喊到 m 的那个会员出列,不再回到圈中,从他的下一个人开始,继续 1, …, m 报数.这样下去,直到剩下最后一个会员为止.这名会员就能获得大奖了.输入格式输入有多组数据.每组数据一行,包含 2 个整数 n(1 <= n <= 100,000), m(1 <= m <= 100,000). n, m 分别表示会员的人数(编号 0, 1, 2, …, n – 1)和数 m(如上文所述).输出对应每组数据,输出最后拿到大奖的会员编号.

1. Linked List(Simulation)

使用链表进行模拟,空间复杂度 O(n),时间复杂度 O(mn),代码如下:

2. Math Problem(Recursive)

把这个序列的 n 个元素记为 0, 1, 2, 3, …, n – 1。把一个 m 与 n 的组合得到的结果记为 f(n, m)。

假定第一次去掉的元素为 k,k的值为 (m – 1) % n,则去掉之后,序列剩余元素为0, 1, …, k + 1, k + 2, …, n – 1。顺序地,序列事实上变换为了k + 1, k + 2, …, n – 1, 0, 1, …, k – 1。这时候,这个序列的结果还是函数 f 吗?由于排列的顺序发生了变化,同时 n 减掉了 1,我们把这个结果记为 g(n – 1, m)。但是,第二个排列是第一个排列计算中的一个过程,所以结果应该是相同的,即:

f(n, m) = g(n – 1, m)

这时我们只需要寻找 g(n – 1, m) 与 f(n – 1, m) 建立联系,即可得到 f 的递归法则。它们的不同其实是元素的排列起始点不同,由于它们都是连续的,则只要找到两个排列之间的偏移量即可。将两个排列的顺序列举如下:

0 -> k + 1

1 -> k + 2

n – k – 2 -> n – 1

n – k – 1 -> 0

n – 2 -> k – 1

可以看出,右侧的结果是左侧加上 k + 1 再对 n 取余的结果。所以,对于 f 和 g 有:

f(n, m) = g(n – 1, m) = (f(n – 1, m) + k + 1)%n

显然,对于 n = 1 的情况,最后一定剩下的是元素 0。到这里我们可以得到 f(n, m)的递归公式:

对于 n > 1 f(n, m) = (f(n – 1, m) + k + 1) % n

对于 n = 1 f(n, m) = 0

这种递归的空间复杂度为 O(1),时间复杂度为 O(n),代码如下:

输出流的缓冲区

Intro

这段时间,不断听到一年级的萌新们在问,fflush是什么函数。为什么有了它就可以正常地输入输出,反而就会少些什么。

首先需要说明的是,在 C 语言中,不应该依赖 fflush 函数解决 I/O 问题。因为它只在某些编译器上有效。很多比较主流的编译器,比如 gcc,就并不支持这个这个函数。它是对C语言标准的扩充,虽然在 C99 中有定义,但不对所有编译器有效。

我们从文档中查到的这个函数的声明方法是 int fflush(FILE *stream); ——这里我们可以认为这个 int 类型的返回值其实是担当了 bool 类型的作用,用来判断这个操作是否成功。

具体的执行情况是:如果参数是输出流(output stream),fflush函数会把还没有输出的数据都传给这个流指向的文件。举例: fflush(stdout); 。否则,fflush 函数的行为即为未定义行为,如果发生了写入相关的错误,返回值是EOF(-1)。

下面,我们从 C++ 的角度研究一些和缓冲区有关的问题。

Output Buffer

尝试用代码向输出流添加一条输出指令,如: os << “Set output to output buffer.”; 。执行的效果可能是立刻就被打印了出来,也有可能没什么反应。这里我们只是给输出流发送指令,而并不是常规意义上的 cout,使得情况可能有所不同。假想以下情况:操作系统需要同时处理一系列输出流的输出到设备操作,而设备的写操作可能非常耗时(比如打印机等输出时需要耗时的操作),操作系统每接到一次输出流的指令就做一次操作显然浪费了系统资源和操作时间。于是,操作系统把这些指令暂时放在输出缓冲区(output buffer)里,接收到缓冲刷新的指令后,统一将多个类似的操作指令组合成单一的系统级写操作。这样,性能得到了极大的提升。

刷新缓冲的原因可能有:

  1. main 函数的 return 操作让程序正常地结束
  2. 缓冲区满,如果不刷新缓存则其他的指令不能进入缓冲区
  3. 利用操纵符显式刷新缓冲区(比如熟悉的 std::endl)
  4. 利用操纵符设置流的状态并清空缓冲区(比如 unitbuf,如果对这个东西不熟悉,那么更熟悉一些的 cerr 的内容都是默认设置  unitbuf  的)
  5. 一个输出流被关联到另一个流(比如 cin 和 cerr 都关联到 cout,因此这两种操作都会使得 cout 的缓冲区刷新)

下面介绍几个实例:

当然,每次输出都和缓冲区打交道是很累的。这时,我们可以借用 unitbuf 指令,告诉系统之后的所有的输出都自带 flush 操作,直到我们给某次输出 nounitbuf,比如:

Worthy to Say

很多情况下,萌新们辛辛苦苦写了很多代码却见不到输出。一方面可能是确实没有输出内容,还有可能是程序崩溃导致这些输出被滞留在缓冲区里面等待打印了。所以,调试的时候必须保证输出缓冲区的数据确实被刷新了,否则就会一直纠结于为什么没有输出结果这个问题上(我个人建议先写出一份可行的版本,也就是编译和运行都没有问题的,之后添加复杂的功能以满足需求)。

One More Thing

在交互较多的情况下,cin 之前必须有 cout 信息输出到设备,这时我们可以把输入流关联到输出流,让每次输入之前都刷新输出缓冲,示例:

HDOJ-2552 三足鼎立——数学不是白学的

描述

MCA 山中人才辈出,洞悉外界战火纷纷,山中各路豪杰决定出山拯救百姓于水火,曾以题数扫全场的威士忌,曾经高数九十九的天外来客,曾以一剑铸十年的亦纷菲,歃血为盟,盘踞全国各个要塞(简称全国赛)遇敌杀敌,遇佛杀佛,终于击退辽军,暂时平定外患,三人位置也处于稳态。

可惜辽誓不甘心,辽国征南大将军<耶律 javac++>欲找出三人所在逐个击破,现在他发现威士忌的位置 s,天外来客的位置 u,不过很难探查到亦纷菲 v 所在何处,只能知道三人满足关系:

arctan(1 / s) = arctan(1 / u) + arctan(1 / v)

定义 f(s,u,v)=v*u-s*u-s*v 的值为”三足鼎立”

<耶律 javac++>想计算<三足鼎立>的值

输入

首先输入一个 t,表示有 t 组数据,跟着 t 行:

输入 s, u(s <= 12^3; u <= 2^20; s, u, v > 0), s, u, v 均为实数

输出

输出 v * u – s * u – s * v 的值,为了简单起见,如果是小数,直接取整

比如:答案是 1.7,则输出 1。

分析

第一感觉,这题一定是个纯模拟,反正 arctan 是可以算的。

不过,仔细想过之后发现,这个题反而是用 arctan 好好的骗了我们一把——当你看到 f(s,u,v) = v * u – s * u – s * v 的时候是不是反映到了 tan 的运算法则呢?

所以,我觉得这是一道数学题。

运算

中等数学告诉我们

tan(a + b) = (tan(a) + tan(b)) / (1 – tan(a) * tan(b)) (1)

tan(arctan(a)) = a (2)

有了以上两个基本准则,我们针对 arctan(1 / s) = arctan(1 / u) + arctan(1 / v)进行推导:

tan(arctan(1 / s)) = tan(arctan(1 / u) + arctan(1 / v))

1 / s=(1 / u + 1 / v) / (1 – 1 / u * v)

1 / s – 1 / s * u * v = 1 / u + 1 / v

v * u – s * u – s * v = 1

所以,我们居然推导出了这道题的结果直接为1!数感拯救世界!

代码