到webank以后一直在做sdk相關(guān)的開發(fā),包括微動力,云客服,以及云刷臉項目.其中遇到一些常見的sdk中開發(fā)應(yīng)該避免的誤區(qū),以及一些可能會踩到的坑.
重中之重的前綴
iOS中使用OC開發(fā),由于OC沒有命名空間概念,很大問題是重名問題.有以下幾個地方都需要注意添加前綴,前綴一定要有辨識度,類名、宏定義、枚舉、通知、類別等命名時加靜態(tài)庫統(tǒng)一特殊前綴,以避免命名沖突:
- 自定義的類: WBFaceSDKxxxxxxClass
- category的方法名最好添加: wbfacesdk_xxxxMethod()
- 對于項目中的c、c++中的方法,需要加前綴,全局c方法: WBFaceSDKCGRectXXXX()
- 全局常量,包括字符串等等:常見的有通知名稱WBFaceSDKxxxxxNotification
- 使用typedef/NS_ENUM/NS_OPTIONS定義的struc或enum的名稱
第三方公用庫或靜態(tài)庫不要打入SDK進行編譯
我們常用的網(wǎng)絡(luò)庫使用AFNetworking,SDWebImage,雖然引入到項目中,但是.m不要勾選"target membership"選項,只需要引入.h就OK.或者通過cocoapod,創(chuàng)建sdk工程,通過cocopads管理項目依賴.
常見的第三方框架,比如libssl.a,opencv.a,libcrypto.a等等,拖入工程的時候"Add to Target"(taget membership)都不要打勾.也就是sdk對這些公共庫有依賴關(guān)系,但是最終生成的sdk不包含公共庫的源碼文件.
盡量減少使用Category
iOS中Category使用的很頻繁,如果靜態(tài)庫里面使用了category,由于OC的runtime和靜態(tài)度的靜態(tài)資源的加載問題.需要進行以下操作:
那么需要在Build settings
->Other Linker Flags
中添加-ObjC
參數(shù),解決運行時unrecognized selector send to instance
的crash錯誤.
此外,如果在category的.m文件中只有一個category分類,此時需要額外添加參數(shù)-all_load
或者使用-force_load /path/to/sdk
來強制加載sdk.為了不使用這個參數(shù),最好的方法是在.m文件中,寫一個空的Class來占位:
// Use dummy class for category in static library.
#ifndef DUMMY_CLASS
#define DUMMY_CLASS(name) \
@interface DUMMY_CLASS_ ## name : NSObject @end \
@implementation DUMMY_CLASS_ ## name @end
#endif
//使用示例:
//UIColor+YYAdd.m
#import "UIColor+YYAdd.h"
DUMMY_CLASS(UIColor+YYAdd)
@implementation UIColor(YYAdd)
...
@end
使用
-all_load
或者-force_load xxx
在編譯期間非常耗時.
為了減少耗時,本人是將通用的類創(chuàng)建成常用的全局函數(shù),static函數(shù),inline函數(shù)(注意添加前綴)
支持通用平臺arm,x86等
我們可以通過如下命令查詢具體.a或者framework文件支持的平臺:lipo -info /path/to/.a
or lipo -info /*.framework/xxx
.
平時項目開發(fā)中,可能使用第三方提供的靜態(tài)庫.a,如果.a提供方技術(shù)不成熟,使用的時候就會出現(xiàn)問題,例如:
- 在真機上編譯報錯:
No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386)
. - 在模擬器上編譯報錯:
No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=armv7s, VALID_ARCHS=armv7 armv6)
.
要解決以上問題,就要了解一下Apple移動設(shè)備處理器指令集相關(guān)的一些細節(jié)知識。
通常我們生成.a文件或者static framework默認配置的Valid Architectures
支持arm64,armv6,ramv7s.sdk也就只能在真機上跑,如果需要在模擬器中跑,需要使其支持x86_64``i386
.
Xcode中指令集相關(guān)選項(Build Setting中):
- Architectures: 工程被編譯成可支持哪些指令集類型,而支持的指令集越多,就會編譯出包含多個指令集代碼的數(shù)據(jù)包,對應(yīng)生成二進制包就越大,也就是ipa包會變大.默認為 Standard architectures(armv7,arm64)
- Valid Architectures:限制可能被支持的指令集的范圍,也就是Xcode編譯出來的二進制包類型最終從這些類型產(chǎn)生,而編譯出哪種指令集的包,將由Architectures與Valid Architectures(因此這個不能為空)的交集來確定.
例如:Valid Architectures設(shè)置的支持arm指令集版本有:armv7/armv7s/arm64,對應(yīng)的Architectures設(shè)置的支持arm指令集版本有:armv7s,這時Xcode只會生成一個armv7s指令集的二進制包。
- Build Active Architecture Only:指定是否只對當前連接設(shè)備所支持的指令集編譯.當這個屬性設(shè)置是yes時候,一般為了debug速度更快,只編譯當前的architecture版本.默認debug:yes, release:no.
在我們制作sdk時候,要做到比較大的兼容性(iPhone 4,iOS7以上),可以進行如下設(shè)置:
- ValidArchitectures設(shè)置為:armv7|armv7s|arm64|i386|x86_64
- Architectures設(shè)置不變(或根據(jù)你需要): armv7|arm64
然后分別選擇iOS設(shè)備和模擬器進行編譯,最后找到相關(guān)的.a進行合包.(xctool或者pod幫助打包).如果是手動創(chuàng)建framework,那么使用如下腳本
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
open "${DEVICE_DIR}"
open "${SRCROOT}/Products"
fi
同時,如果第三方庫不支持模擬式盡量使用預(yù)編譯指令,屏蔽第三方庫:
#if TARGET_IPHONE_SIMULATOR//模擬器
#elif TARGET_OS_IPHONE//真機
#endif
arm64:iPhone5S以上| iPad Air| iPad mini2(iPad mini with Retina Display)
armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)
armv7:iPhone3GS|iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4
i386 :5及以下模擬器
x86_64:5s及以上模擬器
Debug模式,必要Log輸出與crash日志收集
在sdk中要提供完整的Log信息的輸出,尤其是錯誤日志,并且如何處理這個錯誤的步驟.當要收集crash日志時,可以通過系統(tǒng)NSException中提供的NSSetUncaughtExceptionHandler和NSGetUncaughtExceptionHandler方法,收集一些簡單的crash日志信息,然后將收集到的crash日志同步到服務(wù)端.
具體如何進行Log模塊的設(shè)計,crash日志簡單上報暫時沒有很好的方案.
涉及UI界面問題
需要考慮到界面旋轉(zhuǎn)帶來的潛在問題.布局時候盡量使用autolayout.對于必須使用硬編碼的地方才使用硬編碼.
對于硬編碼,獲取屏幕的寬度和高度,與普通的位置不一樣:
#define ScreenHeight MAX([[UIScreen mainScreen] bounds].size.height,[[UIScreen mainScreen] bounds].size.width)//獲取屏幕高度,兼容性測試
#define ScreenWidth MIN([[UIScreen mainScreen] bounds].size.height,[[UIScreen mainScreen] bounds].size.width)//獲取屏幕寬度,兼容性測試
所有的viewController的BaseVC中最好增加以下方法僅支持豎屏:
#pragma mark - viewController orientation
- (UIInterfaceOrientationMask)supportedInterfaceOrientations//支持哪些方向
{
return UIInterfaceOrientationMaskPortrait;
}
/**
初始化自己的方向, 這個在旋轉(zhuǎn)屏幕時候非常重要
If you do not implement this method, the system presents the view controller using the current orientation of the status bar.
說明如果我們沒有override這個方法,系統(tǒng)會根據(jù)當前statusbar來決定當前使用的orientation
*/
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation//默認顯示的方向
{
return UIInterfaceOrientationPortrait;
}
/**
如果返回NO,則無論你的項目如何設(shè)置,你的ViewController都只會使用preferredInterfaceOrientationForPresentation的返回值來初始化自己的方向,如果你沒有重新定義這個函數(shù),那么它就返回父視圖控制器的preferredInterfaceOrientationForPresentation的值。
*/
- (BOOL)shouldAutorotate//是否支持旋轉(zhuǎn)屏幕
{
return NO;
}
所有資源提供到bundle中,命名問題
...
sdk測試完整性
...
SDK中多次使用緩存狀態(tài)問題
在調(diào)用SDK時,一定要先清理SDK原有的配置狀態(tài),緩存狀態(tài)等等!!!!
參考文獻
IOS生成同時支持armv7,armv7s,i386,x86_64,arm64的靜態(tài)庫.a文件
iOS開發(fā)~制作同時支持armv7,armv7s,arm64,i386,x86_64的靜態(tài)庫.a