CodingTour
ARTS #15

Algorithm

本周选择的算法题是:Palindrome Number

规则如下:

Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward.

Example 1:

Input: 121
Output: true

Example 2:

Input: -121
Output: false
Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.

Example 3:

Input: 10
Output: false
Explanation: Reads 01 from right to left. Therefore it is not a palindrome.

Follow up:

Coud you solve it without converting the integer to a string?

Solution

我实现的方案:

class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x < 0:
            return False

        reverse_number = 0
        temp = x
        while temp != 0:
            reverse_number = reverse_number * 10 + temp % 10
            temp //= 10

        return x == reverse_number

Runtime:72 ms,快过 61.77%。

Memory:14.2 MB,低于 6.50%。

看了下官方的 Solution,发现不用将数字完全 reverse,只需要 reverse 一半即可,这样时间复杂度和空间复杂度都能得到降低,而且不会有溢出的风险:

class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x < 0 or (x != 0 and x % 10 == 0):
            return False

        reverse_number = 0
        while x > reverse_number:
            reverse_number = reverse_number * 10 + x % 10
            x //= 10

        return x == reverse_number or x == reverse_number // 10

Runtime:60 ms,快过 93.18%。

Memory:14.1 MB,低于 6.50%。

Review

Application Security Musts for every iOS App

关于 iOS App 存储安全的话题。主要观点如下:

敏感数据直接存到 UserDefaults 是不安全的

UserDefaults 就是一个简单的 plist 文件而已,它存储在应用的 Preferences 文件夹下,你可以很容易通过 iMazing 这类程序查看它的内容,明文存进 UserDefaults 的内容没有任何安全性可言。

Keychain 比 UserDefaults 更安全

Apple 提供了一个内置的安全服务 - Keychain,它的特点如下:

  • 数据并不存在 App 的沙盒中,就算 App 被删除了,Keychain 中的数据依然存在
  • 可以实现 group 间的共享
  • 数据是经过 Keychain 加密存放的

进一步提高安全性

放进 Keychain 就万事大吉了吗?当然不是,明文存储还是有风险的:

  • 如果攻击者破解了 Keychain 的安全机制,那存储的敏感数据自然就会有暴露的风险
  • 如题攻击者通过网络抓包等方式,嗅探到你的请求参数,而它又是明文传输的,这也增加了暴露的风险

所以,在存储敏感数据时,无论存到哪里,最好将其加密存储。

只进行 MD5 可能是不够的,攻击者可以提前计算好容易被用作密码的 MD5 摘要,然后使用彩虹表攻击,为了避免这种攻击方式,我们可以采用这样的存储策略:

func saveEncryptedPassword(_ password: String, for account: String) {
    let salt = Array("salty".utf8)
    let key = try! HKDF(password: Array(password.utf8), salt: salt, variant: .sha256).calculate().toHexString()
    keychainService.save(key, for: account)
}
  • 加盐存储
  • 计算 hash
  • 使用像 HKDF(基于 HMAC 的 KDF 算法)这样的算法

这样可以进一步提高本地存储的安全性。除此之外,客户端和服务器之间通信时,要确保算法的一致性,为了防止攻击者模拟请求,可以通过 timestamp 等数据动态生成盐或者签名算法。

Tip

git-lfs 不能直接 track 一个目录,如果有想要 track 类似 *.framework 这样的情况,可以用:

git lfs track **/*.framework/*

Share

分享几个关于 Xcode 编译优化的辅助功能

在 Xcode 里显示编译时长

defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES

设置好后就能看到总的编译时长了

找出 导致编译慢的代码(Swift)

Other Swift Flags 里设置如下 flag:

  • -Xfrontend -warn-long-function-bodies=<millisecond>
  • -Xfrontend -warn-long-expression-type-checking=<millisecond>

在编译时就能看到一些警告信息了:

设置 Build active architecture Only

一般来说,在 Debug 时设置为 YES,Release 时设置为 NO