iOS10 再談 CAAnimationDelegate 相關協議的適配

發表於2016-12-03

前言

之前寫過這樣一篇文章:

iOS10 CAAnimationDelegate的簡單適配

文中提到了在iOS10中,關於系統API裡CAAnimationDelegate協議的一些變化以及一種較為妥協的的適配寫法。此文是基於上文的一個補充和完善,並提出一些相對更為完善的寫法。
為了方便和節省大家的時間,先在此直接給出下文中提到的一種最簡單的寫法,對於探究過程沒有興趣的可以直接忽略下文(但是還是強烈建議看看,一方面這麼寫一項都不能預設是有重要原因的,另一方面如果你還有其他想法,歡迎據此給出指正或建議)

1.起因

寫這篇文章的起因是前陣子在寫一個demo時,用到了CALayerDelegate的相關協議方法,並遇到了和CAAnimationDelegate一樣的情況,警告。
檢視API,也看到了類似的變化:

111468630-fe81ab84a767d2c3
iOS10以後的CALayerDelegate(截圖自xcode8.1相關API)

再對比下10以前的:

121468630-f9a91929a5c2e428
iOS10之前的CALayerDelegate(截圖自xcode7.3.1相關API)

同樣的變化,由基類的分類這種非正式協議,變成了正式的協議宣告。再看下這個代理的前後變化:

131468630-6cb7320bcaef66f2
iOS10以後
141468630-14ad136af03f5aff
iOS10之前

本來也是直接按照之前的方式來解決這個警告的,但是細想下來,不知道這樣的變化還有多少,只是目前遇到的,都只是QuartzCore框架下的。這或許是蘋果在規範自己的API?

2.關於這裡的適配

這裡說的適配,並不是像適配某一個方法是否相容某一系統版本那樣的適配,因為那種版本適配不做的話,影響的是APP的相容性。而這裡的適配,並不會影響APP的相容性,有試過直接用低版本真機編譯這些未經適配的協議程式碼,執行並不會有什麼影響。具體機理我並不能說太清楚,但是畢竟這只是一種針對原有API的修改,而不是新增。(其實我一直是很好奇像NS_AVAILABLE_IOS這類相關的巨集標示的方法和其指定的低版本系統不相容的原因,是系統執行這些程式碼時會遇到什麼問題嗎?求解。)

那麼做這樣的適配意義又何在?
開發相容。

一個團隊協作開發,可能因為各種原因,每個人的xcode版本並不能保持一致(適配iOS7及以下的機器用xcode8除錯不了、低版本系統的mac裝不了新的xcode……)。
還有就是我們在寫一些開源三方時,不做這樣的相容適配,有些使用低版本xcode的開發者,在使用你的程式碼時,可能會因此報錯,從而造成一些不必要的麻煩。

3.更好的適配寫法

這裡的更好,是相對之前那篇文章來的。
之前那樣寫,是因為不能確定開發者xcode版本,從而不能直接使用__IPHONE_10_0這類巨集。

3.1.嘗試的方案一

既然不能確定__IPHONE_10_0巨集是否存在,從而不能直接寫__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0這樣的判定語句(因為若__IPHONE_10_0巨集不存在,這個邏輯恆成立從而無效),那麼改為判等應該就沒問題了,可惜因為10.1也出來了,只能增加或邏輯。不過,這樣做,同樣帶來了維護的代價,系統肯定是要持續迭代的,每次迭代都要新增或條件,這樣做麻煩不說,風險也是遠大於之前的做法的。。。

3.2.改進的方案二

既然是因為無法判斷__IPHONE_10_0巨集是否存在,那麼就以此為條件豈不是正好?而且這類巨集是隨版本迭代持續遞增的,那麼只要__IPHONE_10_0存在,應該就可以確認xcode版本至少是8,畢竟__IPHONE_10_1肯定是之後出現的。
然而,經過實際實驗……在xcode7.3.1上依舊報錯:CALayerDelegate協議不存在,#ifdef條件分支依舊參與編譯了……why???
首先這樣的邏輯應該是沒問題的,那就檢視xcode7.3.1的API吧。果然,在CABase.h檔案中發現了這樣的一段定義:

151468630-a349f6f329b5b10e
xcode7.3.1中CABase.h檔案中的一段定義

什麼鬼???9.3.1還要涉及10.0了?難怪之前的條件可以成立……
再次翻看xcode8.1中關於CABase.h中的相關部分,好嘛,這些已經全都沒有了,整個標頭檔案的巨集定義改了一大半,基本“面目全非”了,果然是善變啊……

3.3.完善的方案三

有了之前兩次嘗試,方案三應運而生,也就是文章開頭的那個了:

這就是為什麼開頭有提到,一項都不能預設,否則後果見方案二。
這裡除了判斷__IPHONE_10_0巨集是否定義,還判斷了當前系統的版本是否是大於10.0的,其實主要是為了判斷xcode的版本是否是大於8.0的(第2部分提到的開發適配)。
#if defined(__IPHONE_10_0)這個等效於#if defined __IPHONE_10_0,也就是我們通常寫的#ifdef __IPHONE_10_0的簡化後的單條件版本,用#if defined更為靈活,可以判斷多個條件。
當然這裡也可以根據習慣拆開寫,即先寫#ifdef __IPHONE_10_0,再寫#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0,只是多了幾步,意思相同。

至此,針對這部分的適配結束了。最終的方案應該是較為完善的了,目前也沒有想到更合適的了。實際使用測試,還沒有發現什麼問題,如果你遇到了,歡迎及時反饋。

參考文章:
1.C語言的條件編譯#if, #elif, #else, #endif、#ifdef, #ifndef
2.#if、#ifdef、#if defined之間的區別
3.#if defined和#if !defined(c語言的巨集定義)

·轉載請宣告·

相關文章