iOS 摸魚週報 #54 | Apple 輔助功能持續創新

語言: CN / TW / HK

本期概要

  • 話題:Apple 在輔助功能上持續創新;IAP 自動續訂提價通知更新
  • 面試模塊:學習 OOMDetector 中的 CRC64 應用實踐
  • 優秀博客:iOS 內存
  • 學習資料:一份英語進階指南
  • 開發工具:一款 macOS 上的 純文本編輯器 CotEditor

本期話題

Apple 在輔助功能上的又一創新

@zhangferry:作為一款受眾非常廣的產品,針對特殊人羣的輔助功能就顯得尤為重要,不得不説 Apple 對輔助功能的重視程度和探索精神都是值得尊敬的。最近 Apple 又公佈了一些基於軟硬件和機器學習帶來的輔助功能提升。

針對盲人和視力障礙的人羣:Apple 基於配有 LiDAR 的設備可以探測到前方是否有門,門距離自己有多遠,甚至要通過推還是拉的方式開門都能識別出來。

針對行動不便的人羣:有一項 iPhone 結合 Apple Watch 的功能,藉助於 Apple Watch 的 Mirroring 功能,可以用手機遠程操作 Apple Watch。同時 Apple Watch 也有提升,通過 AssistiveTouch 技術,可以讓 Apple Watch 識別特定手勢,像是手指兩次捏合的手勢可以用於接電話、拍照、暫停音樂等。

針對聽力障礙的人羣:在 iPhone、iPad、Mac 配備了實時字幕功能,不只是針對 Facetime,對於任意音頻內容,包括外部 App 都可以使用。樣式是在設備頂部展示一個文本轉義框,字體大小還可調整。

同時 VoiceOver 也進一步完善,增加了 20 多個地區語言的支持。

IAP 自動續訂提價通知更新

@zhangferry:自動續訂是 Apple Store 付費產品使用最廣泛的一個訂閲選項。當一個已經被用户續訂的產品進行提價時,Apple 會通過郵件、推送和 App 內消息的形式告知用户,如果用户未選擇接受變更價格,下個續訂週期就會默認中斷。這可能會導致部分用户的不理解,影響其體驗。該項改進意在增加一些條件,使得提價之後的續訂週期可以默認延續。這個條件是:每年提價不超過一次,同時訂閲價格上調不超過 5 美元和 50%,或者年度訂閲價格上調不超過 50 美元和 50%,並且是在法律允許的範圍內。該舉措仍會通知到用户價格的變更。

面試解析

整理編輯:Hello World

學習 OOMDetector 中的 CRC64 應用實踐

OOMDetector 中對 CRC64 的應用講解實際應用時的一些變體操作。示例代碼如下:

```cpp

define POLY64REV 0x95AC9329AC4BC9B5ULL

static uint64_t crc_table[8][256];

void init_crc_table_for_oom(void) { uint64_t c; int n, k; static int first = 1; if(first) { first = 0; // 針對單個字節值生成單表 for (n = 0; n < 256; n++) { c = (uint64_t)n; for (k = 0; k < 8; k++) { // LSB 右移生成邏輯, 主要適用於小端模式 f (c & 1) c = (c >> 1) ^ POLY64REV; else c >>= 1; } crc_table[0][n] = c; } // 生成不同權重的 CRC 值 for (n = 0; n < 256; n++) { c = crc_table[0][n]; for (k = 1; k < 8; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; } } } }

uint64_t rapid_crc64(uint64_t crc, const char buf, uint64_t len) { register uint64_t buf64 = (uint64_t )buf; register uint64_t c = crc; register uint64_t length = len; // 取反 c = ~c; while (length >= 8) { c ^= buf64++; // 根據不同權重的字節數據查表 c = crc_table[0][c & 0xff] ^ crc_table[1][(c >> 8) & 0xff] ^ \ crc_table[2][(c >> 16) & 0xff] ^ crc_table[3][(c >> 24) & 0xff] ^\ crc_table[4][(c >> 32) & 0xff] ^ crc_table[5][(c >> 40) & 0xff] ^\ crc_table[6][(c >> 48) & 0xff] ^ crc_table[7][(c >> 56) & 0xff]; length -= 8; } // 這裏註釋的內容,是單字節計算的邏輯,即每次計算一個字節,可能最早的 OOMDetector 採用的是該計算方式。 // buf = (char )buf64; // while (length > 0) { // crc = (crc >> 8) ^ crc_table[0][(crc & 0xff) ^ buf++]; // length--; // } // 取反 c = ~c; return c; } ```

主要有兩步操作,CRC 生成表以及 CRC 查表。以這兩步出發學習一下 CRC 實際應用中的變體以及目的。

生成表-多表級聯

有別於《#53 週報》中的單表查詢方式,OOMDetectorcrc_table定義為crc_table[8][256]二維矩陣的多表查詢。其中單維度的表仍然以字節大小(8 bit)作為位寬生成,即單個表大小為 2 ^ 8 = 256crc_table[8]表示不同權重的單表, 這種方式稱為 CRC 位域多表查詢 。

CRC 位域多表查表方法與傳統的 CRC 查表方法最大的不同在於多表級聯壓縮表格空間。

如果是傳統單表查詢,一次性查詢雙字節數據的 CRC,需要單表大小為 256 * 256,採用多表級聯只需要 2 *256,實現了極大的空間壓縮。

更詳細的原理可以參考《CRC位域多表查表方法》,這裏只理解該優化依賴的核心性質:crc_table[A ^ B] = crc_table[A] ^ crc_table[B]

從 CRC 計算的本質出發,其實就是依次的計算每一 bit 位的餘數,而餘數的結果值,只和 POLY計算的次數和順序有關。

我們以示例 0xBC來拆解這個計算過程:

  1. 採用右移的計算方式,左移和右移的區別在下小結中講解。1011 1010(0xBA) 計算 CRC Table 簡化為:

    cpp 1011 1010 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7)

  2. 現在 0xBA 根據異或性質分解為 0xB0 ^ 0x0A

  3. 我們單獨計算 0xB00x0A的 CRC 值,來看他們的計算過程 ```cpp 0xB0 = 0b 1011 0000; CRC[0xB0] = 1011 0000 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ 0>>4 ^ 0>>5 ^ 0>>6 ^ 0>>7);

    0x0A = 0b 0000 1010; CRC[0x0A] = 0000 1010 ^ (0 ^ 0>>1 ^ 0>>2 ^ 0>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7) ```

  4. 上面兩個公式做異或,最終結果值和 CRC[0xBA]相等

    ```cpp CRC[0xB0] ^ CRC[0x0A] = 1011 0000 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ 0>>4 ^ 0>>5 ^ 0>>6 ^ 0>>7) ^ 0000 1010 ^ (0 ^ 0>>1 ^ 0>>2 ^ 0>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7);

    // 由於 0 異或任何值還是原值,結果可以簡化為: 1011 0000 ^ 0000 1010 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7); ```

  5. 即證明 CRC 性質:crc_table[A ^ B] = crc_table[A] ^ crc_table[B]成立。

CRC 級聯查表另一個需要解決的就是多字節數據的權重問題,上面證明了 CRC 性質可行性,但是在查表時為了方便,使用的索引並非是直接拆解的數據,例如 CRC[0x AA BB] = CRC[0x AA 00] ^ CRC[0xBB] ,兩次查表索引分別為 0xBB 和0xAA,並非是 0xBB 和 0xAA 00

權重就是指的由 CRC[0xAA] 計算 CRC[0xAA 00] ... CRC[0xAA 00 00 00 00 00 00 00] 等數據,實現也很簡單,可以看做是已知單表crc_table[256]的值,求數據的值,即 init_crc_table_for_oom()中第二個 for 的目的。

生成表-單表反向計算(reversed)

OOMDetector 生成單表時區別於傳統的 MSB 左移(<<) 計算方式,採用的是 LSB 右移(>>) 生成方式。原因是 iOS 的主機字節序是小端模式,但是一般規範中要求數據在網絡傳輸過程中採用網絡字節序(大端模式)。

一個系統中針對每一個字節內的 bit 位也是有順序的,稱為位序。

位序一般和主機字節序是一致的,例如一個數據 0x11 22 在 iOS 內存中的存儲為 0x22 0x11,實際 0x11 = 0b 0001 0001的存儲順序也是逆序的,表示為 0b 1000 1000

為了按照網絡字節序傳輸規範作為計算 CRC 順序的依據,小端的機器上在使用 CRC 時都採用右移計算,即 0b 1000 1000按照右移順序依次計算 0 0 0 1 0 0 0 1,這樣保證了規範性,無論其他 server 接收端是大端模式還是小端模式,在拿到數據後自己按照主機字節序重新計算即可。

反向計算最重要的一點:由於計算順序反向,所以 POLY生成多項式的值相對於傳統給定的生成多項式值,也要做位序的反向生成新的 POLY值。

查表

rapid_crc64()查表中一次計算了 8 字節數據的 CRC 值,根據crc_table[A ^ B] = crc_table[A] ^ crc_table[B]性質,查表操作以對應權重的字節數據在相應的級聯表中查找值即可,具體到每一個級聯表,和單字節的查表邏輯一致。最終結果是各個權重字節數據的異或結果。

示例計算數據為 0x AA BB CC DD 11 22 33 44 ,在小端模式是逆序存儲的,所以在計算 CRC 值時是從低字節到高字節(即從右到左)順序計算的。分解計算步驟來分析:

  1. 根據異或計算的性質 0x AA BB CC DD 11 22 33 44 = 0x44 ^ 0x33 00 ^ 0x 22 00 00 ^ 0x11 00 00 00 ... ^ 0xAA 00 00 00 00 00 00 0
  2. 結合 CRC 性質 CRC[0x AA BB CC DD 11 22 33 44] = CRC[0x44] ^ CRC [0x33 00] ^ .... CRC[0xAA 00 00 00 00 00 00 00]
  3. 根據二維級聯表,查找每一個字節的 CRC,CRC[0x 3300] = crc_table[1][0x33]。注意這裏是用 0x33作為索引計算數據 0x33 00的值,和計算數據 0x33 是有區別的,所以在生成表時需要做二次遍歷以生成不同權重的單表值。(這裏的權重可以理解為單字節數據在 8 個字節中的位置,從左到右為 MSB => LSB)

OOMDetector 在查表之前和查表之後都做了一次取反操作 c = ~c,該變體的目的是解決普通 CRC 無法區分只有起始 0 的個數不同的兩個數據。(暫時未理解這個目的,所以直接引用 wiki 中的解釋)

《循環宂餘校驗-wiki》:移位寄存器可以初始化成1而不是0。同樣,在用算法處理之前,消息的最初n個數據位要取反。這是因為未經修改的CRC無法區分只有起始0的個數不同的兩條消息。而經過這樣的取反過程,CRC就可以正確地分辨這些消息了。

優秀博客

整理編輯:皮拉夫大王在此

本期博客主題:iOS 內存。如果你對以下幾個問題不瞭解的話,推薦閲讀本期的博客。 - 什麼是 MMU?什麼是 clean/dirty/compressed memory? - 申請 malloc(1),malloc_size 是多少? - 小內存釋放,內存會立即還給系統嗎? - TCMalloc 主要解決什麼問題?

  1. iOS Memory 內存詳解 (長文) -- 來自掘金:RickeyBoy

@皮拉夫大王:本文主要介紹了 iOS 內存相關的基礎知識,可以幫助讀者建立內存知識全景圖。我們可以帶着問題去閲讀這篇文章:(1)、虛擬內存是如何映射到物理內存的?(2)、clean/dirty memory是如何區分的?一塊 dirty memory 的單位大小是多少?

  1. 深入理解內存分配 -- 來自網易數帆:阿凡達

@皮拉夫大王:內存分配的硬核文章,內容很有意思。通過閲讀這篇文章,首先我們會了解 free 的過程,順帶也就能理解作者舉的例子:str[0]='a' 報錯非 bad_access 的原因了。另外作者列舉了多種替換系統默認內存分配方式,這也是比較有意思的一點。

  1. Matrix-iOS 內存監控 -- 來自騰訊雲:微信終端團隊

@皮拉夫大王:來自微信的 matrix 內存監控原理介紹工具,能夠抓取每個對象生成時的堆棧。與 OOMDetector 的原理一致,但是性能上更勝一籌。如此大量且高頻的堆棧抓取和保存,matrix 是如何做優化的?可以通過閲讀本文來了解細節。

  1. TCMalloc解密 -- 來自:Wallen's Blog

@皮拉夫大王:對《深入理解內存分配》中提到的 TCMalloc 感興趣的可以繼續閲讀這篇文章。

見聞

這一週閲讀/瀏覽到的有趣的資訊。

1、Mac 與遊戲無緣,M1 來了也沒用 -- 來自公眾號:APPSO

@遠恆之義:提到遊戲,具體到 PC 端的遊戲,Mac 電腦基本是沾不上邊的。傳統意義上的 PC 遊戲,指的是在 Windows 電腦上玩的遊戲,Mac 電腦只是一個生產力工具。我曾下載過戰網客户端,在 Mac 上玩暴雪遊戲《爐石傳説》,但這樣原生支持 Mac 平台的廠商並不多。我也用過騰訊 START 雲遊戲,對網絡要求很高,在 MacBook 上玩《英雄聯盟》,打團時的延遲尚能接受。最近拿 PS5 手柄在 iPad 上試玩 Arcade 遊戲,遊戲體驗還不錯,也能兼容 Mac 平台。那麼,為什麼 Mac 距離主流遊戲市場這麼遠呢?M1 芯片的到來,能給 Mac 遊戲帶來新的機遇嗎?作者在文中給出了答案。

2、對 iPod 説再見,我想帶你走進無數人的「青春記憶」 -- 來自少數派:宛潼

@遠恆之義:停產了,售罄了,下架了,擁有 20 年壽命的 iPod,走到了生命的終點。作為一款音樂播放器,iPod 的產品線十分豐富。無論是初代經典 iPod Classic,還是短暫嘗試的 iPod mini,還有被用户吐槽最多的 iPod shuffle,多次探索新形態、新功能和新技術的 iPod nano,功能強大的 iPod touch,這些都已成為了歷史,讓人懷戀。擁有過 iPod 的你,是否也有「爺青結」的感歎呢。就讓本文的作者帶你一起了解 iPod 相關的彩蛋產品,喚起你的「青春記憶」吧。

3、Bash tips: Colors and formatting (ANSI/VT100 Control sequences)

@zhangferry:終端常見的輸出樣式是黑白,但實際上它還可以設置顏色和一些簡單的格式,這些樣式的配置可以利用 ANSI 轉義碼。整個過程分為兩步,第一,讓 Bash 識別轉義碼,第二步,指定轉義碼顏色。看一個例子:

bash $ echo -e "\e[31mRed Text\e[0m"

這個命令輸出內容是紅色文本的 Red Text,參數含義説明如下:

| Option | Description | | :----: | :----------------------------------------------------------: | | -e | 開啟反斜槓的轉義功能 | | \e[ | 它是 Bash 識別轉義的起始標誌符。\e 是 ASCII 碼中的 ESC,表示控制符,8 進製表示為 \033,也是常見用法。[ 是轉義序列開始標記符 | | 31m | 由 ANSI 轉義碼定義,31 表示紅色,m 表示顏色取值結束 | | \e[0m | \e 含義同上,開始識別 ANSI,0 表示重置設置 |

4、Airport

@zhangferry:TestFlight 是 Apple 用於提供內測功能的應用,一般我們只是用它測試自己的應用或者已安裝應用的升級嚐鮮。TestFlight 版本的 App 有這些優點:審核相比 AppStore 要鬆很多、功能限制少、對於需要內購的產品可以 0 元嚐鮮。但是對於外界還有哪些不為人熟知的 TF 版應用我們是不清楚的,Airport 要做的事情就是這個,你可以在這裏根據分類和搜索挑選你喜歡的應用參與測試。

5、大疆無人機模擬飛行

@zhangferry:這是大疆出的無人機模擬飛行體驗網站,打開之後等待頁面渲染完成就可以在一個虛擬城市裏體驗操縱無人機的感覺。該模擬還配備了視角切換、拍照、錄像等物理機具備的所有幾乎所有功能。同時還有物理撞擊的模擬,也就是説如果你飛行中撞到了建築物,無人機也是會墜毀的,第一視角的墜毀效果做的很不錯。

學習資料

整理編輯:zhangferry

英語進階指南

地址:http://babyyoung.gitbook.io/english-level-up-tips/

英語是程序員繞不過去的一項技能,雖然我們可能從小學就開始接觸英語了,但直到畢業工作,英語能夠不成為學習障礙還是一件不容易的事情。這其中的差別很大成分可以歸結為學習方法,這份文檔就是這樣一個注重方法和可操作性的英語學習指南。

工具推薦

整理編輯:CoderStar

CotEditor

地址:http://coteditor.com/

軟件狀態:免費

軟件介紹

適用於 macOS 的純文本編輯器,輕巧、整潔並且功能強大。

CotEditor

關於我們

iOS 摸魚週報,主要分享開發過程中遇到的經驗教訓、優質的博客、高質量的學習資料、實用的開發工具等。週報倉庫在這裏:http://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的內容推薦可以通過 issue 的方式進行提交。另外也可以申請成為我們的常駐編輯,一起維護這份週報。另可關注公眾號:iOS成長之路,後台點擊進羣交流,聯繫我們,獲取更多內容。

往期推薦

iOS 摸魚週報 #53 | 遠程辦公正在成為趨勢

iOS 摸魚週報 #52 | 如何規劃個人發展

iOS 摸魚週報 #51 | 遊戲版號恢復發放

iOS 摸魚週報 第五十期