iOS 摸鱼周报 #54 | Apple 辅助功能持续创新
本期概要
- 話題:Apple 在輔助功能上持續(xù)創(chuàng)新;IAP 自動續(xù)訂提價通知更新
- 面試模塊:學(xué)習(xí) OOMDetector 中的 CRC64 應(yīng)用實踐
- 優(yōu)秀博客:iOS 內(nèi)存
- 學(xué)習(xí)資料:一份英語進(jìn)階指南
- 開發(fā)工具:一款 macOS 上的 純文本編輯器 CotEditor
本期話題
Apple 在輔助功能上的又一創(chuàng)新
@zhangferry:作為一款受眾非常廣的產(chǎn)品,針對特殊人群的輔助功能就顯得尤為重要,不得不說 Apple 對輔助功能的重視程度和探索精神都是值得尊敬的。最近 Apple 又公布了一些基于軟硬件和機(jī)器學(xué)習(xí)帶來的輔助功能提升。
針對盲人和視力障礙的人群:Apple 基于配有 LiDAR 的設(shè)備可以探測到前方是否有門,門距離自己有多遠(yuǎn),甚至要通過推還是拉的方式開門都能識別出來。
針對行動不便的人群:有一項 iPhone 結(jié)合 Apple Watch 的功能,借助于 Apple Watch 的 Mirroring 功能,可以用手機(jī)遠(yuǎn)程操作 Apple Watch。同時 Apple Watch 也有提升,通過 AssistiveTouch 技術(shù),可以讓 Apple Watch 識別特定手勢,像是手指兩次捏合的手勢可以用于接電話、拍照、暫停音樂等。
針對聽力障礙的人群:在 iPhone、iPad、Mac 配備了實時字幕功能,不只是針對 Facetime,對于任意音頻內(nèi)容,包括外部 App 都可以使用。樣式是在設(shè)備頂部展示一個文本轉(zhuǎn)義框,字體大小還可調(diào)整。
同時 VoiceOver 也進(jìn)一步完善,增加了 20 多個地區(qū)語言的支持。
IAP 自動續(xù)訂提價通知更新
@zhangferry:自動續(xù)訂是 Apple Store 付費產(chǎn)品使用最廣泛的一個訂閱選項。當(dāng)一個已經(jīng)被用戶續(xù)訂的產(chǎn)品進(jìn)行提價時,Apple 會通過郵件、推送和 App 內(nèi)消息的形式告知用戶,如果用戶未選擇接受變更價格,下個續(xù)訂周期就會默認(rèn)中斷。這可能會導(dǎo)致部分用戶的不理解,影響其體驗。該項改進(jìn)意在增加一些條件,使得提價之后的續(xù)訂周期可以默認(rèn)延續(xù)。這個條件是:每年提價不超過一次,同時訂閱價格上調(diào)不超過 5 美元和 50%,或者年度訂閱價格上調(diào)不超過 50 美元和 50%,并且是在法律允許的范圍內(nèi)。該舉措仍會通知到用戶價格的變更。
面試解析
整理編輯:Hello World
學(xué)習(xí) OOMDetector 中的 CRC64 應(yīng)用實踐
以 OOMDetector 中對 CRC64 的應(yīng)用講解實際應(yīng)用時的一些變體操作。示例代碼如下:
```cpp
define POLY64REV 0x95AC9329AC4BC9B5ULL
static uint64t crctable[8][256];
void initcrctableforoom(void) { uint64t c; int n, k; static int first = 1; if(first) { first = 0; // 針對單個字節(jié)值生成單表 for (n = 0; n < 256; n++) { c = (uint64t)n; for (k = 0; k < 8; k++) { // LSB 右移生成邏輯, 主要適用于小端模式 f (c & 1) c = (c >> 1) ^ POLY64REV; else c >>= 1; } crctable[0][n] = c; } // 生成不同權(quán)重的 CRC 值 for (n = 0; n < 256; n++) { c = crctable[0][n]; for (k = 1; k < 8; k++) { c = crctable[0][c & 0xff] ^ (c >> 8); crctable[k][n] = c; } } } }
uint64t rapidcrc64(uint64t crc, const char *buf, uint64t len) { register uint64t *buf64 = (uint64t *)buf; register uint64t c = crc; register uint64t length = len; // 取反 c = ~c; while (length >= 8) { c ^= *buf64++; // 根據(jù)不同權(quán)重的字節(jié)數(shù)據(jù)查表 c = crctable[0][c & 0xff] ^ crctable[1][(c >> 8) & 0xff] ^ \ crctable[2][(c >> 16) & 0xff] ^ crctable[3][(c >> 24) & 0xff] ^\ crctable[4][(c >> 32) & 0xff] ^ crctable[5][(c >> 40) & 0xff] ^\ crctable[6][(c >> 48) & 0xff] ^ crctable[7][(c >> 56) & 0xff]; length -= 8; } // 這里注釋的內(nèi)容,是單字節(jié)計算的邏輯,即每次計算一個字節(jié),可能最早的 OOMDetector 采用的是該計算方式。 // buf = (char *)buf64; // while (length > 0) { // crc = (crc >> 8) ^ crc_table[0][(crc & 0xff) ^ *buf++]; // length--; // } // 取反 c = ~c; return c; } ```
主要有兩步操作,CRC 生成表以及 CRC 查表。以這兩步出發(fā)學(xué)習(xí)一下 CRC 實際應(yīng)用中的變體以及目的。
生成表-多表級聯(lián)
有別于《#53 周報》中的單表查詢方式,OOMDetector將 crc_table定義為crc_table[8][256]二維矩陣的多表查詢。其中單維度的表仍然以字節(jié)大小(8 bit)作為位寬生成,即單個表大小為 2 ^ 8 = 256,crc_table[8]表示不同權(quán)重的單表, 這種方式稱為 CRC 位域多表查詢 。
CRC 位域多表查表方法與傳統(tǒng)的 CRC 查表方法最大的不同在于多表級聯(lián)壓縮表格空間。
如果是傳統(tǒng)單表查詢,一次性查詢雙字節(jié)數(shù)據(jù)的 CRC,需要單表大小為 256 * 256,采用多表級聯(lián)只需要 2 *256,實現(xiàn)了極大的空間壓縮。
更詳細(xì)的原理可以參考《CRC位域多表查表方法》,這里只理解該優(yōu)化依賴的核心性質(zhì):crc_table[A ^ B] = crc_table[A] ^ crc_table[B]。
從 CRC 計算的本質(zhì)出發(fā),其實就是依次的計算每一 bit 位的余數(shù),而余數(shù)的結(jié)果值,只和 POLY計算的次數(shù)和順序有關(guān)。
我們以示例 0xBC來拆解這個計算過程:
采用右移的計算方式,左移和右移的區(qū)別在下小結(jié)中講解。1011 1010(0xBA) 計算 CRC Table 簡化為:
cpp 1011 1010 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7)
現(xiàn)在 0xBA 根據(jù)異或性質(zhì)分解為 0xB0 ^ 0x0A。
我們單獨計算 0xB0 和 0x0A的 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) ```
上面兩個公式做異或,最終結(jié)果值和 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 異或任何值還是原值,結(jié)果可以簡化為: 1011 0000 ^ 0000 1010 ^ (poly ^ 0>>1 ^ poly>>2 ^ poly>>3 ^ poly>>4 ^ 0>>5 ^ poly>>6 ^ 0>>7); ```
即證明 CRC 性質(zhì):crc_table[A ^ B] = crc_table[A] ^ crc_table[B]成立。
CRC 級聯(lián)查表另一個需要解決的就是多字節(jié)數(shù)據(jù)的權(quán)重問題,上面證明了 CRC 性質(zhì)可行性,但是在查表時為了方便,使用的索引并非是直接拆解的數(shù)據(jù),例如 CRC[0x AA BB] = CRC[0x AA 00] ^ CRC[0xBB] ,兩次查表索引分別為 0xBB 和0xAA,并非是 0xBB 和 0xAA 00。
權(quán)重就是指的由 CRC[0xAA] 計算 CRC[0xAA 00] ... CRC[0xAA 00 00 00 00 00 00 00] 等數(shù)據(jù),實現(xiàn)也很簡單,可以看做是已知單表crc_table[256]的值,求數(shù)據(jù)的值,即 init_crc_table_for_oom()中第二個 for 的目的。
生成表-單表反向計算(reversed)
OOMDetector 生成單表時區(qū)別于傳統(tǒng)的 MSB 左移(<<) 計算方式,采用的是 LSB 右移(>>) 生成方式。原因是 iOS 的主機(jī)字節(jié)序是小端模式,但是一般規(guī)范中要求數(shù)據(jù)在網(wǎng)絡(luò)傳輸過程中采用網(wǎng)絡(luò)字節(jié)序(大端模式)。
一個系統(tǒng)中針對每一個字節(jié)內(nèi)的 bit 位也是有順序的,稱為位序。
位序一般和主機(jī)字節(jié)序是一致的,例如一個數(shù)據(jù) 0x11 22 在 iOS 內(nèi)存中的存儲為 0x22 0x11,實際 0x11 = 0b 0001 0001的存儲順序也是逆序的,表示為 0b 1000 1000。
為了按照網(wǎng)絡(luò)字節(jié)序傳輸規(guī)范作為計算 CRC 順序的依據(jù),小端的機(jī)器上在使用 CRC 時都采用右移計算,即 0b 1000 1000按照右移順序依次計算 0 0 0 1 0 0 0 1,這樣保證了規(guī)范性,無論其他 server 接收端是大端模式還是小端模式,在拿到數(shù)據(jù)后自己按照主機(jī)字節(jié)序重新計算即可。
反向計算最重要的一點:由于計算順序反向,所以 POLY生成多項式的值相對于傳統(tǒng)給定的生成多項式值,也要做位序的反向生成新的 POLY值。
查表
在 rapid_crc64()查表中一次計算了 8 字節(jié)數(shù)據(jù)的 CRC 值,根據(jù)crc_table[A ^ B] = crc_table[A] ^ crc_table[B]性質(zhì),查表操作以對應(yīng)權(quán)重的字節(jié)數(shù)據(jù)在相應(yīng)的級聯(lián)表中查找值即可,具體到每一個級聯(lián)表,和單字節(jié)的查表邏輯一致。最終結(jié)果是各個權(quán)重字節(jié)數(shù)據(jù)的異或結(jié)果。
示例計算數(shù)據(jù)為 0x AA BB CC DD 11 22 33 44 ,在小端模式是逆序存儲的,所以在計算 CRC 值時是從低字節(jié)到高字節(jié)(即從右到左)順序計算的。分解計算步驟來分析:
OOMDetector 在查表之前和查表之后都做了一次取反操作 c = ~c,該變體的目的是解決普通 CRC 無法區(qū)分只有起始 0 的個數(shù)不同的兩個數(shù)據(jù)。(暫時未理解這個目的,所以直接引用 wiki 中的解釋)
《循環(huán)冗餘校驗-wiki》:移位寄存器可以初始化成1而不是0。同樣,在用算法處理之前,消息的最初n個數(shù)據(jù)位要取反。這是因為未經(jīng)修改的CRC無法區(qū)分只有起始0的個數(shù)不同的兩條消息。而經(jīng)過這樣的取反過程,CRC就可以正確地分辨這些消息了。
- CRC位域多表查表方法
優(yōu)秀博客
整理編輯:皮拉夫大王在此
本期博客主題:iOS 內(nèi)存。如果你對以下幾個問題不了解的話,推薦閱讀本期的博客。 - 什么是 MMU?什么是 clean/dirty/compressed memory? - 申請 malloc(1),malloc_size 是多少? - 小內(nèi)存釋放,內(nèi)存會立即還給系統(tǒng)嗎? - TCMalloc 主要解決什么問題?
@皮拉夫大王:本文主要介紹了 iOS 內(nèi)存相關(guān)的基礎(chǔ)知識,可以幫助讀者建立內(nèi)存知識全景圖。我們可以帶著問題去閱讀這篇文章:(1)、虛擬內(nèi)存是如何映射到物理內(nèi)存的?(2)、clean/dirty memory是如何區(qū)分的?一塊 dirty memory 的單位大小是多少?
@皮拉夫大王:內(nèi)存分配的硬核文章,內(nèi)容很有意思。通過閱讀這篇文章,首先我們會了解 free 的過程,順帶也就能理解作者舉的例子:str[0]='a' 報錯非 bad_access 的原因了。另外作者列舉了多種替換系統(tǒng)默認(rèn)內(nèi)存分配方式,這也是比較有意思的一點。
@皮拉夫大王:來自微信的 matrix 內(nèi)存監(jiān)控原理介紹工具,能夠抓取每個對象生成時的堆棧。與 OOMDetector 的原理一致,但是性能上更勝一籌。如此大量且高頻的堆棧抓取和保存,matrix 是如何做優(yōu)化的?可以通過閱讀本文來了解細(xì)節(jié)。
@皮拉夫大王:對《深入理解內(nèi)存分配》中提到的 TCMalloc 感興趣的可以繼續(xù)閱讀這篇文章。
見聞
這一周閱讀/瀏覽到的有趣的資訊。
1、Mac 與游戲無緣,M1 來了也沒用 -- 來自公眾號:APPSO
@遠(yuǎn)恒之義:提到游戲,具體到 PC 端的游戲,Mac 電腦基本是沾不上邊的。傳統(tǒng)意義上的 PC 游戲,指的是在 Windows 電腦上玩的游戲,Mac 電腦只是一個生產(chǎn)力工具。我曾下載過戰(zhàn)網(wǎng)客戶端,在 Mac 上玩暴雪游戲《爐石傳說》,但這樣原生支持 Mac 平臺的廠商并不多。我也用過騰訊 START 云游戲,對網(wǎng)絡(luò)要求很高,在 MacBook 上玩《英雄聯(lián)盟》,打團(tuán)時的延遲尚能接受。最近拿 PS5 手柄在 iPad 上試玩 Arcade 游戲,游戲體驗還不錯,也能兼容 Mac 平臺。那么,為什么 Mac 距離主流游戲市場這么遠(yuǎn)呢?M1 芯片的到來,能給 Mac 游戲帶來新的機(jī)遇嗎?作者在文中給出了答案。
2、對 iPod 說再見,我想帶你走進(jìn)無數(shù)人的「青春記憶」 -- 來自少數(shù)派:宛潼
@遠(yuǎn)恒之義:停產(chǎn)了,售罄了,下架了,擁有 20 年壽命的 iPod,走到了生命的終點。作為一款音樂播放器,iPod 的產(chǎn)品線十分豐富。無論是初代經(jīng)典 iPod Classic,還是短暫嘗試的 iPod mini,還有被用戶吐槽最多的 iPod shuffle,多次探索新形態(tài)、新功能和新技術(shù)的 iPod nano,功能強(qiáng)大的 iPod touch,這些都已成為了歷史,讓人懷戀。擁有過 iPod 的你,是否也有「爺青結(jié)」的感嘆呢。就讓本文的作者帶你一起了解 iPod 相關(guān)的彩蛋產(chǎn)品,喚起你的「青春記憶」吧。
3、Bash tips: Colors and formatting (ANSI/VT100 Control sequences)
@zhangferry:終端常見的輸出樣式是黑白,但實際上它還可以設(shè)置顏色和一些簡單的格式,這些樣式的配置可以利用 ANSI 轉(zhuǎn)義碼。整個過程分為兩步,第一,讓 Bash 識別轉(zhuǎn)義碼,第二步,指定轉(zhuǎn)義碼顏色??匆粋€例子:
bash $ echo -e "\e[31mRed Text\e[0m"
這個命令輸出內(nèi)容是紅色文本的 Red Text,參數(shù)含義說明如下:
| Option | Description | | :----: | :----------------------------------------------------------: | | -e | 開啟反斜杠的轉(zhuǎn)義功能 | | \e[ | 它是 Bash 識別轉(zhuǎn)義的起始標(biāo)志符。\e 是 ASCII 碼中的 ESC,表示控制符,8 進(jìn)制表示為 \033,也是常見用法。[ 是轉(zhuǎn)義序列開始標(biāo)記符 | | 31m | 由 ANSI 轉(zhuǎn)義碼定義,31 表示紅色,m 表示顏色取值結(jié)束 | | \e[0m | \e 含義同上,開始識別 ANSI,0 表示重置設(shè)置 |
4、Airport
@zhangferry:TestFlight 是 Apple 用于提供內(nèi)測功能的應(yīng)用,一般我們只是用它測試自己的應(yīng)用或者已安裝應(yīng)用的升級嘗鮮。TestFlight 版本的 App 有這些優(yōu)點:審核相比 AppStore 要松很多、功能限制少、對于需要內(nèi)購的產(chǎn)品可以 0 元嘗鮮。但是對于外界還有哪些不為人熟知的 TF 版應(yīng)用我們是不清楚的,Airport 要做的事情就是這個,你可以在這里根據(jù)分類和搜索挑選你喜歡的應(yīng)用參與測試。
5、大疆無人機(jī)模擬飛行
@zhangferry:這是大疆出的無人機(jī)模擬飛行體驗網(wǎng)站,打開之后等待頁面渲染完成就可以在一個虛擬城市里體驗操縱無人機(jī)的感覺。該模擬還配備了視角切換、拍照、錄像等物理機(jī)具備的所有幾乎所有功能。同時還有物理撞擊的模擬,也就是說如果你飛行中撞到了建筑物,無人機(jī)也是會墜毀的,第一視角的墜毀效果做的很不錯。
學(xué)習(xí)資料
整理編輯:zhangferry
英語進(jìn)階指南
地址:https://babyyoung.gitbook.io/english-level-up-tips/
英語是程序員繞不過去的一項技能,雖然我們可能從小學(xué)就開始接觸英語了,但直到畢業(yè)工作,英語能夠不成為學(xué)習(xí)障礙還是一件不容易的事情。這其中的差別很大成分可以歸結(jié)為學(xué)習(xí)方法,這份文檔就是這樣一個注重方法和可操作性的英語學(xué)習(xí)指南。
工具推薦
整理編輯:CoderStar
CotEditor
地址:https://coteditor.com/
軟件狀態(tài):免費
軟件介紹:
適用于 macOS 的純文本編輯器,輕巧、整潔并且功能強(qiáng)大。
關(guān)于我們
iOS 摸魚周報,主要分享開發(fā)過程中遇到的經(jīng)驗教訓(xùn)、優(yōu)質(zhì)的博客、高質(zhì)量的學(xué)習(xí)資料、實用的開發(fā)工具等。周報倉庫在這里:https://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的內(nèi)容推薦可以通過 issue 的方式進(jìn)行提交。另外也可以申請成為我們的常駐編輯,一起維護(hù)這份周報。另可關(guān)注公眾號:iOS成長之路,后臺點擊進(jìn)群交流,聯(lián)系我們,獲取更多內(nèi)容。
往期推薦
iOS 摸魚周報 #53 | 遠(yuǎn)程辦公正在成為趨勢
iOS 摸魚周報 #52 | 如何規(guī)劃個人發(fā)展
iOS 摸魚周報 #51 | 游戲版號恢復(fù)發(fā)放
iOS 摸魚周報 第五十期
總結(jié)
以上是生活随笔為你收集整理的iOS 摸鱼周报 #54 | Apple 辅助功能持续创新的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Bypass Preventing CS
- 下一篇: 和时间做朋友