天天看點

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

前言

分享iOS開發中遇到的問題,和相關的一些思考,本次内容包括:UITableView滾動問題、ARC、xcconfig、Push證書。

正文

UITableView

UITableView在reloadData 的時候,如果height的高度發生較大變化,contentOffset無法保持原來的大小時,會發生滾動的效果。如果直接reloadData再setContentOffset:設定位置,仍會出現滾動的效果。

如果需要去除該滾動效果,可以在reloadData之後,調用scrollToRowAtIndexPath并設定animated:NO,最後再用setContentOffset:微調位置。

同理,如果需要在reloadData後,手動scroll到header時,可用同上的解決方案。

UITableView還有類似的問題,如果清單項過多時,scrollToRowAtIndexPath有時并不準确,比如有1000行時滾動到第500行,此時可能會出現滾到501或者499行的情況。

究其原因,是因為UITableView不會調用1~499行所有的heightFor和cellFor方法,是以無法準确計算出來位置。

從這裡去分析,如果需要滾動到準确的位置,可以用estimatedRowHeight的屬性,設定和行高一樣的高度;在行高各不相同的場景,可以設定estimatedRowHeight為大緻的數字,在scrollToRowAtIndexPath之後通過setContentOffset:微調位置。

ARC

Automatic Reference Counting(ARC)是編譯器特性,由編譯器插入對象記憶體管理代碼,實作記憶體管理。

如果僅僅是retain/release的管理,非常容易了解,但是插入的代碼如何實作weak、strong這些運作時特性?

最近同僚遇到一個問題,以下代碼會crash:

他實作了一個editingButton的getter,同時在dealloc的時候将其移除;

如果editingButton在整個生命周期都沒有初始化時,則在dealloc使用getter會觸發初始化,然後在下面的

weakify(self);

這一行crash。

- (void)dealloc
{
    [self.editingView removeFromSuperview];
    [self.editingButton removeFromSuperview];  // crash
}

- (UIButton *)editingButton
{
    if (!_editingButton)
    {
        _editingButton = [UIButton buttonWithType:UIButtonTypeCustom];
        ......
        weakify(self); // CRASH
        [_editingButton ss_addEventHandler:^(id  _Nonnull sender) {
                  ......
        } forControlEvents:UIControlEventTouchDown];
    }
    return _editingButton;
}           

複制

閃退的堆棧如下

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

在ARC的文檔中找到閃退的方法,其中有一段描述如下:

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

當dealloc開始的時候,weakSelf的指針應該都已經被重置為nil;如果在dealloc的函數中再次初始化weakSelf指針會出現異常。

另外,在dealloc方法執行屬性的getter方法也是不合理,因為屬性的getter方法大都包括如果未建立就建立并初始化的邏輯。

ARC的文檔 這份文檔也是非常好的ARC學習資料。

xcconfig

xcconfig是用來儲存

build setting

鍵值對的檔案,裡面是一些純文字;

//:configuration = Debug
PRODUCT_BUNDLE_IDENTIFIER = com.loyinglin.dev
DISPLAY_NAME = 測試标題
PRODUCT_NAME = Learning
GCC_TREAT_WARNINGS_AS_ERRORS = YES

//:configuration = Debug
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 SSDEBUG=1           

複制

比如這裡配置是一份debug的xcconfig,其中

PRODUCT_BUNDLE_IDENTIFIER = com.loyinglin.dev

的鍵值會在編譯的時候生效。

xcconfig有什麼用?

一個Xcode工程,一定會有Debug的開發環境和Release的釋出環境,可能會有Testflight的灰階環境、DailyBuild的持續內建環境、XXLanguage的多語言環境、TestCoverage的覆寫率測試環境、IAP的内購測試環境等;每個環境所用的證書不同,APP安裝後顯示的名字不同,provision file也不同等等。

一種方案是使用Target來解決,公用的部分設定在project,每個環境根據各自特點自定義某些設定;這樣帶來的後果是target數量增多明顯,而target增多帶來的後果是當需要新增extension的時候會工作量巨大,并且多環境的管理難度加劇。

另外一種方案是使用Configuration來區分環境,而xcconfig就是用來管理Configuration的檔案。

如何建立和使用xcconfig?

1、在Xcode中建立檔案,輸入config,選擇configuration settings file;這一步是建立xcconfig的檔案。

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

2、在Xcode中選中工程,在configurations中選擇需要配置的選項,這裡以debug為例,點選後選擇剛剛已經建立的xcconfig,則可以把xcconfig和debug的編譯選項綁定在一起。

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

如果你用了cocoaPod,你會發現這一項已經有了CocoaPod建立xcconfig,如果選擇了自己建立的xcconfig,則會編譯失敗;

此時可以在自己建立的xcconfig頭檔案中加入以下代碼:

#include "Pods/Target Support Files/Pods-YourName/Pods-YourName.debug.xcconfig"           

複制

注意需要修改成自己的工程名。

3、在build setting選中某個配置項,cmd+c複制然後到xcconfig的檔案中,cmd+v就可以複制配置項到xcconfig中。

注意如果這個配置項在build setting已經有自定義值,需要将其删除,原因下面解釋。

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

image.png

xcconifg的配置和工程預設配置、手動在build setting配置有什麼差別?

配置的結果優先級不同,我的了解是:

a、project預設配置是最低優先級,因為是最基礎的配置;

b、target配置基于project,但target預設會添加一些配置,優先級比上面高;

c、xcconfig的配置是target某個config的配置,優先級比上面高;

d、target的build setting中直接添加的配置項,優先級比上面高;

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

手動配置項

知道上面的關系後,我們可以解決使用xcconifg時,CI 打包xcconifg配置項不生效的問題:

檢查是否對應配置項是否在target的build setting中直接添加;

如果需要新增某個configuration,可以直接duplicate已有的configuration,但是如果使用Pods需要重新pod install,以生成對應的pod工程的配置項,否則會出現下圖的報錯:

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

找不到對應庫,因為新的configuration沒有設定對應的file

Push 證書

.p12是連接配接蘋果APNs伺服器的證書(公鑰+私鑰);

.cer 是蘋果的證書檔案(公鑰);

.pem是OpenSSL的證書檔案(公鑰+私鑰);

當我們生成push證書時,其實就是将我們本地的p12通過腳本,導出對應的pem檔案;

下面是一段常用的腳本:

P12_CERT=AppStorePush.p12 # p12證書檔案
PASSWD=loying # p12密碼

EXPORT_CERT=AppStorePush.pem # 導出pem證書
EXPORT_KEY=AppStorePushWithKey.pem  # 導出的pem私鑰,有密碼
EXPORT_KEY_UNENCRY=AppStorePushWithoutKey.pem # 導出的pem私鑰,無密碼
EXPORT_KEY_AND_CERT=AppStore_ck.pem # 含有證書和私鑰的pem

openssl pkcs12 -clcerts -nokeys -out ${EXPORT_CERT} -in ${P12_CERT} -passin pass:${PASSWD} # 導出證書

openssl pkcs12 -nocerts -passout pass:${PASSWD} -out ${EXPORT_KEY} -in ${P12_CERT} -passin pass:${PASSWD} #導出私鑰,有密碼

openssl rsa -in ${EXPORT_KEY} -passin pass:${PASSWD} -out ${EXPORT_KEY_UNENCRY} # 導出私鑰,無密碼

cat ${EXPORT_CERT} ${EXPORT_KEY_UNENCRY} > ${EXPORT_KEY_AND_CERT}   # 證書和私鑰合起來

openssl s_client -connect gateway.push.apple.com:2195 -cert ${EXPORT_CERT} -key ${EXPORT_KEY_UNENCRY}   # 測試 push證書

# gateway.push.apple.com
# gateway.sandbox.push.apple.com           

複制

在調試Push的時候,以下這個軟體(App Store可以下載下傳)非常便捷:

iOS開發筆記(十一)— UITableView、ARC、xcconfig、Push

使用時配置好證書(可以點選connect驗證是否連接配接APNs成功),再從iPhone擷取到deviceToken添加到裝置清單,便可以使用推送。

總結

這些都是在項目中遇到的一些問題,UITableView這個是老生常談,ARC那篇文檔是很好的學習資料,xcconfig需要多研究,未來随着版本和管道增多會越來越複雜,Push在Easy APNs Provider這個軟體出來後就很好測試,再也不用登入信鴿去手動配置Push。

新的一年,繼續搬磚和學習。