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 摸魚週報 第五十期