引子html
無心間,看到5年前,Android大佬子勰寫的,關於SDK開發方面的文章(SDK那些事(總綱)), 不禁得喚起本身開發iOS SDK的回憶;本文簡單總結下本身開發SDK方面的經驗;前端
SDK(Software Development Kit)能夠最大程度實現代碼和功能的複用,爲業務開發提供一個很是好的支持;這裏的業務能夠是內部業務,也能夠是外部業務;android
簡單來講,所謂SDK開發,本質是服務提供;不只須要寫好代碼,還要完善代碼以外的事情,任重道遠git
通常而言,新起一個SDK必然有其深入的業務背景;研發同窗對SDK要解決的問題和SDK的特殊要求,瞭解地越詳細越好;常見的要求有:github
XX
KB:【其餘】 SDK可能長期維護 或 多人開發,確立好基本代碼規範,能保障SDK的代碼質量;這些規範本質上是一些共識和約束,如:面試
SDK中可能包含不一樣的模塊功能,而不一樣的業務方須要的模塊可能不一樣;對SDK中模塊進行拆分,保證業務方儘量引入的是他們須要的代碼;跨域
通常使用Cocoapods建立Pod庫的,在podspec
中定義好模塊,爲每一個模塊清晰定義好包含的代碼和資源,以及外部依賴(靜態庫 or 靜態庫);這樣能夠將模塊之間實現代碼和資源的物理隔離;緩存
關於Cocoapods建立Pod庫更多細節能夠參考Cocoapods使用小記,至因而公有Pod仍是私有Pod根據實際狀況定;安全
建立公有Pod庫或者私有Pod庫原理是同樣的;不同的是:二者的版本索引查詢方式不同,公有庫的podspec由CocoaPods/Specs管理,而內部私有使用的pod庫須要公司內部創建一個倉庫來管理podspec微信
Pod庫中,代碼放在Classes
目錄下,圖片資源放在Assets
目錄下;
Classes
目錄按模塊劃分第一級目錄,如AModule
、BModule
、CModule
等,其中每一個模塊Code再劃分二級目錄,如ModuleService、View、Controller、Model、API等;具體的代碼文件存放在這些二級目錄中;其中ModuleService
中代碼是要對外暴露的,其餘預期外部不可見;
資源方面,也按模塊細節;主要的資源是圖片資源,在Assets
目錄下新加AModule.xcassets
、BModule.xcassets
、CModule.xcassets
等;
SDK使用者們關注接口是夠好用,設計好接口,讓你的SDK使用體驗加分;
接口功能儘可能職責單一,接口須要的參數不要太多;若是參數多,可使用Model將業務參數封裝下;提供Model的default實現
;接口名,參數名使用駝峯命名,最好見名知義;
接口中每一個參數類型要明確,不要出現id
、NSDictionary
這樣的類型;避免業務隨意傳參,增長SDK內部對參數校驗難度;也減小業務方對參數的困惑;
接口內第一件事情是要作參數校驗,不符合預期狀況,Debug模式直接Assert,及早把問題拋出;Release模式要記錄到日誌並上報,提早返回,避免後續出現迷惑問題,增長排查問題成本;
@optional
關鍵字,沒有的話默認要實現;協議和協議中具體方法的做用要增長上註釋;NS_ENUM
)定義,對應的每一個值增長註釋;WXErrCode
不要透傳給業務方,封裝一下,對外暴露的是咱們的狀態碼;podspec
中的version
、代碼中的版本號;能夠實現個腳本,在編譯時候,統一修改這三處的版本號信息;開發SDK和開發完整項目同樣,要有需求評審、技術評審、排期,開發,自測,提測、測試驗收等環節 ;不一樣功能在不一樣feature分支上開發,每一個feature功能驗證經過後能夠合入主幹分支;合入主幹後,由主幹分支發佈版本;
發佈的SDK使用二進制的形式;SDK使用二進制形式,不只能提示項目項目編譯速度,也能保護好源碼;若是業務方須要SDK源碼,須要向SDK負責人申請權限;
Code Review
;其實這對業務方是個很高的要求,須要業務方至少有一我的對SDK有比較全面的瞭解;autorealsepool
,下降內存峯值,避免 OOMNSCache
代替 NSMutableDictionary
,使用NSPurgableData
代替NSData
;weak strong dance
來解決 Block 中的循環引用,代理(delegate)使用weak
修飾;+ (instancetype)sharedInstance
,內部使用dispatch_once
保證alloc和init只執行一次,這種是粗髮式單例
,並不能保證絕對單例;+(instancetype)allocWithZone
、-(instancetype)copyWithZone
和-(instancetype)mutableCopyWithZone
方法,保證永遠都只會分配一次內存空間,實現真正的單例;do{...}while(0)
構造的宏定義不會受到大括號、分號等的影響,很是建議使用。__attribute__ ((weak))
屬性將其變成weak symbol;weak symbol在連接時候比較特殊:
weak symbol 不作標準方案推薦; 遇到要臨時適配某些業務的特殊case,時間緊急狀況下,能夠"劍走偏鋒";
簡介:有些公開功能使用宏定義的函數形式,能夠在函數中帶上__FILE__
、__LINE__
和__FUNCTION__
這些C語言中預約義符;這樣能夠在發生問題時,更好找到使用宏函數的位置,demo以下:
#define AddFunc(a,b) do { \ addFuncImp(a,b, __FILE__,__LINE__,__FUNCTION__); \ } while(0)
有人問過:爲何不使用[NSThread callStackSymbols]
獲取當前線程堆棧信息,豈不是更好;不使用有3方面考慮:
[NSThread callStackSymbols]
捕獲堆棧信息在符號裁剪狀況下,主模塊中的是內存地址信息,而不是符號信息;__FILE__
、__LINE__
和__FUNCTION__
的成本更低,性能更好;section()
函數簡介:section()
函數是Clang提供的,能夠讀寫二進制段;實際應用中,在編譯階段將一些肯定的常量寫入數據段(__DATA段
),並在運行期根據須要讀取出來;能夠利用此能力實現延遲加載;
在阿里的iOS的BeeHive有相似的使用,以下:
#define BeeHiveMod(name) \ char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name""; #define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))
used
關鍵詞是告訴編譯器,Release下不優化,必須保留這個符號;不然Release
下。連接器會優化掉沒有引用的符號;
簡介:App啓動耗時通常統計pre-main
階段(T1),和main()
函數以後到didFinishLaunchingWithOptions
方法執行完這段(T2)階段;SDK中能夠利用__attribute__ ((constructor))
、__attribute__((destructor))
這兩個函數屬性在pre-main
和after_main
時機作一些事情;
使用這兩個屬性定義函數示範以下:
__attribute__((constructor)) void before_main_xxxx() { //can do something } __attribute__((destructor)) void after_main_xxxx() { //can do something }
須要說明的是:在pre-main
和after_main
時機,千萬不要作耗時操做;在SDK(二進制形式)中使用比較隱蔽,通常狀況下,業務方很難想到或注意到;若是在pre-main時機作了耗時的事情,宿主App啓動體驗就不太好了;
dyld加載過程分四步:
Load dylibs image
、Rebase/Bind image
、Objc setup
和initializers
;其中+load()
在__attribute__((constructor))
以前,他們都在initializers
階段內完成;initializers
以後就是main
函數執行了;
Method Swizzling
是Objective-C中運行時特性之一,本質是在運行時交換方法實現(IMP);SDK有時候須要Method Swizzling
利用hook一些系統(Objective-C
)方法;Method Swizzling
的話,推薦使用RSSwizzle,他是線程安全的Method Swizzling方案,優點是:不須要在+load()
中實現方法交換 並且是 線程安全的;020 持續更新,精品小圈子每日都有新內容,乾貨濃度極高。
結實人脈、討論技術 你想要的這裏都有!
搶先入羣,跑贏同齡人!(入羣無需任何費用)
BAT大廠面試題、獨家面試工具包,
資料免費領取,包括 數據結構、底層進階、圖形視覺、音視頻、架構設計、逆向安防、RxSwift、flutter,
做者:南華Coder
連接:https://juejin.im/post/5e9adea16fb9a03c364f216e