編程隨想:學習技術的三部曲
WHAT“WHAT”也就是“What is it?”——這是最簡單的層次。在這個層次,你要搞清楚某個東東是 什麼樣子的?有什麼用?有什麼功能特性?有什麼語法?……
HOW所謂的“HOW”就是“How to do?”。在這個層次,你要搞清楚某個東西,其內部是如何運作 的?如何實現的?……
WHY一般來說,只有想清楚HOW之後,才能繼續去考慮WHY。所謂的“WHY”,就是搞清楚某個東西為什麼設計成這樣?為什麼不是另外的樣子?這樣的設計有什麼講究?……說實在的,善於問“為什麼”有一定的天賦成分?好像某個科學大牛曾經說過“提出問題有時候 比解決問題更難”。一般來說,只有當你深刻理解了某個東西,才能夠針對這個東東的設計問 出一些問題。所以,先把HOW的問題搞清楚,再來考慮WHY的問題。
關於源碼學習自己的一些感悟
第一層:熟練使用;
第二層:讀懂代碼;
第三層:通曉原理;
第四層:如何設計;
自己學到了什麼,還留有什麼問題;
關於分享
關於線下演講分享和線上文章分享,我一直覺得技術領域要學東西的話線上文章分享是最好的形式,一是它傳播廣,觸達用戶多;二是耗時少,寫一篇文章或看一篇文章都比聽一個分享花的時間少很多;三是可沉澱,讀者可以反復看細節,可以沉澱下來不斷被人搜索到。 – Bang (JSPatch 作者)
YYModel 作者性能優化的幾個 Tip:
緩存Model JSON 轉換過程中需要很多類的元數據,如果數據足夠小,則全部緩存到內存中。
查表當遇到多項選擇的條件時,要盡量使用查表法實現,比如 switch/case,C Array,如果查表條件是對象,則可以用 NSDictionary 來實現。
避免 KVCKey-Value Coding 使用起來非常方便,但性能上要差於直接調用 Getter/Setter,所以如果能避免 KVC 而用 Getter/Setter 代替,性能會有較大提升。
避免 Getter/Setter 調用如果能直接訪問 ivar,則盡量使用 ivar 而不要使用 Getter/Setter 這樣也能節省一部分開銷。
避免多余的內存管理方法在 ARC 條件下,默認聲明的對象是 strong 類型的,賦值時有可能會產生 retain/release 調用,如果一個變量在其生命周期內不會被釋放,則使用 unsafe_unretained 會節省很大的開銷。訪問具有 weak 屬性的變量時,實際上會調用 objc_loadWeak() 和 objc_storeWeak() 來完成,這也會帶來很大的開銷,所以要避免使用 weak 屬性。創建和使用對象時,要盡量避免對象進入 autoreleasepool,以避免額外的資源開銷。
遍歷容器類時,選擇更高效的方法相對於 Foundation 的方法來說,CoreFoundation 的方法有更高的性能,用 CFArrayApplyFunction() 和 CFDictionaryApplyFunction() 方法來遍歷容器類能帶來不少性能提升,但代碼寫起來會非常麻煩。
盡量用純 C 函數、內聯函數使用純 C 函數可以避免 ObjC 的消息發送帶來的開銷。如果 C 函數比較小,使用 inline 可以避免一部分壓棧彈棧等函數調用的開銷。
減少遍歷的循環次數在 JSON 和 Model 轉換前,Model 的屬性個數和 JSON 的屬性個數都是已知的,這時選擇數量較少的那一方進行遍歷,會節省很多時間。
YYModel 源碼學習篇
把不熟練的一些代碼挑出來,弄清楚
NS_ASSUME_NONNULL_BEGIN / NS_ASSUME_NONNULL_END
為了防止寫一大堆 nonnull(不可為nil),Foundation 還提供了一對兒宏,包在裡面的對象默認加 nonnull 修飾符,只需要把 nullable 的指出來就行
1 NS_ASSUME_NONNULL_BEGIN 2 @interface Sark : NSObject@property (nonatomic, copy, nullable) NSString *workingCompany; 3 @property (nonatomic, copy) NSArray *friends; 4 - (nullable NSString *)gayFriend; 5 @end 6 NS_ASSUME_NONNULL_END
#define force_inline inline attribute((always_inline))
1 __attribute__((always_inline))的意思是強制內聯,所有加了__attribute__((always_inline))的函數再被調用時不會被編譯成函數調用而是直接擴展到調用函數體內,比如我定義了函數 2 __attribute__((always_inline)) void a() 3 void b(){a();} 4 b調用a函數的匯編代碼不會是跳轉到a執行,而是a函數的代碼直接在b內成為b的一部分。
普通函數
調用時,會出現流程跳轉來回
首先跳到被調用函數;
被調用函數執行完之後,又會跳會主調函數中;
內聯函數
調用時,直接 將內聯函數的代碼全部復制到調用的地點;
內聯函數的定義必須出現在調用之前;
犧牲內存空間,來提高函數執行速度;
內聯函數的不足
通常,編譯器比程序設計者更清楚對於一個特定的函數是否合適進行內聯擴展;一些情況下,對於程序員指定的某些內聯函數,編譯器可能更傾向於不使用內聯甚至根本無法完成內聯。
代碼比較長的,即使聲明為inline,也不會最終內聯
而有的一些比較短的小函數,即使沒有聲明inline,也會由c/c++編譯器最終內聯
而如果函數使用強制內聯,那麼最終就一定是內聯
對於一些開發中的函數,它們可能從原來的不適合內聯擴展變得適合或者倒過來。盡管內聯函數或者非內聯函數的轉換易於宏的轉換,但增加的維護開支還是使得它的優點顯得更不突出了。
對於基於C的編譯系統,內聯函數的使用可能大大增加編譯時間,因為每個調用該函數的地方都需要替換成函數體,代碼量的增加也同時帶來了潛在的編譯時間的增加。
函數調用中堆棧的個人理解
NSCoder
iOS開發-數據存儲NSCoderNSCoding是一個protocol. 如果實現了NSCoding,需要實現其中的兩個方法:
1 - (void)encodeWithCoder:(NSCoder *)aCoder; 2 - (id)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER
方法中的主要的參數就是NSCoder,它是archivie字節流的抽象類.可以將數據寫入一個coder,也可以從coder中讀取我們寫入的數據. NSCoder是一個抽象類,不能直接使用它來創建對象. 但是可以通過其子類NSKeyedUnarchiver從字節流中讀取數據,NSKeyedArchiver將對象寫入到字節流。
@package
@package變量,對於framework內部,相當於@protected, 對於framework外部,相當於@privat。
這個特性,很適合用於開發第三方的靜態類庫,因為多數人並不希望讓別人知道自己屬性的值。
.(點)和->(箭頭)的區別
.(點語法)是訪問類的屬性,本質是調用set、get方法。
->是訪問成員變量,但成員變量默認受保護,所以常常報錯,手動設為public即可解決
Core Foundation框架和Cocoa Foundation框架區別
Core Foundation框架 (CoreFoundation.framework) 是一組C語言接口,它們為iOS應用程序提供基本數據管理和服務功能。下面列舉該框架支持進行管理的數據以及可提供的服務:
群體數據類型 (數組、集合等)
程序包
字符串管理
日期和時間管理
原始數據塊管理
偏好管理
URL及數據流操作
線程和RunLoop
端口和soket通訊Core Foundation框架和Foundation框架緊密相關,它們為相同功能提供接口,但Foundation框架提供Objective-C接口。如果您將Foundation對象和Core Foundation類型摻雜使用,則可利用兩個框架之間的 “toll-free bridging”。所謂的Toll-free bridging是說您可以在某個框架的方法或函數同時使用Core Foundatio和Foundation 框架中的某些類型。很多數據類型支持這一特性,其中包括群體和字符串數據類型。每個框架的類和類型描述都會對某個對象是否為 toll-free bridged,應和什麼對象橋接進行說明。
CoreFoundation 對於 ios開發的作用
對於corefoundation一直存在疑問,一直感覺他是作為ios開發的底層接口,可是為什麼很多開發者習慣於實用corefoundation接口而不是用NS庫,比如CFUUIDRef,NSUUID,都可以產生UUID,但是看到的大部分代碼都是使用CFUUIDRef,這是為什麼呢,直接使用corefoundation接口有什麼好處呢?
核心是和其他加框架和架構方便“溝通”。The programming interfaces of Core Foundation objects have been designed for ease of use and reuse. At a general level, Core Foundation:
Enables sharing of code and data among various frameworks and libraries
Makes some degree of operating-system independence possible
Supports internationalization with Unicode strings
Provides common API and other useful capabilities, including a plug-in architecture, XML property lists, and preferences
Core Foundation makes it possible for the different frameworks and libraries on OS X to share code and data. Applications, libraries, and frameworks can define C routines that incorporate Core Foundation types in their external interfaces; they can thus communicate data—as Core Foundation objects—to each other through these interfaces.
__bridge 相關知識點
bridge,bridge_transfer和__bridge_retained詳解
__bridge只做類型轉換,但是不修改對象(內存)管理權;
__bridge_retained(也可以使用CFBridgingRetain)將Objective-C的對象轉換為Core Foundation的對象,同時將對象(內存)的管理權交給我們,後續需要使用CFRelease或者相關方法來釋放對象;
__bridge_transfer(也可以使用CFBridgingRelease)將Core Foundation的對象轉換為Objective-C的對象,同時將對象(內存)的管理權交給ARC。
CFMutableDictionaryRef with ARC
如何創建並使用一個 CFMutableDictionaryRef
1 CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2 NSString *key = @"someKey"; 3 NSNumber *value = [NSNumber numberWithInt: 1]; 4 CFDictionarySetValue(myDict, (__bridge void *)key, (__bridge void *)value); 5 id dictValue = (__bridge id)CFDictionaryGetValue(myDict, (__bridge void *)key); 6 CFDictionaryRemoveValue(myDict, (__bridge void *)key);
如何根據 NSMutableDictionary 創建 CFMutableDictionaryRef
1 NSMutableDictionary *myDict = [NSMutableDictionary dictionary]; 2 NSString *key = @"someKey"; 3 NSNumber *value = [NSNumber numberWithInt: 1]; 4 [myDict setObject:value forKey:key]; 5 CFMutableDictionaryRef myCFDict = CFBridgingRetain(myDict); 6 // use myCFDict here 7 CFRelease(myCFDict);
CoreFoundation、CFDictionaryApplyFunction
Context
1 typedef struct { 2 void* object; 3 void* value; 4 }MyConext;
回調c函數
1 static void MyContextCllbackFunc(const void *_key, const void *_value, void *context) { 2 NSLog(@"%@", _key); 3 NSLog(@"%@", _value); 4 NSLog(@"%p", context); }
示例
1 NSDictionary *dict = @{ 2 @"key1" : @"value", 3 @"key2" : @"value", 4 @"key3" : @"value", 5 }; 6 User *user = [User new]; 7 MyConext context = {0}; 8 context.object = (__bridge void *)(user); 9 context.value = @"value"; 10 CFDictionaryApplyFunction((CFDictionaryRef)dict, MyContextCllbackFunc, &context);
輸出
2016-07-13 16:07:29.556 LJFSourceCodeLearn[5521:241563] key1 2016-07-13 16:07:29.556 LJFSourceCodeLearn[5521:241563] value 2016-07-13 16:07:29.556 LJFSourceCodeLearn[5521:241563] 0x7fff51eb8b88 2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] key3 2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] value 2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] 0x7fff51eb8b88 2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] key2 2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] value 2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] 0x7fff51eb8b88
類似還有CFArrayApplyFunction使用
1 NSMutableArray *array = [NSMutableArray new]; 2 for (int i = 0; i < 3; i++) { 3 RuntimeViewController *user = [RuntimeViewController new]; 4 user.name = [NSString stringWithFormat:@"%d%d%d", i,i,i]; 5 [array addObject:user]; 6 } 7 //Context結構體實例 8 MyConext context = {0}; 9 context.object = @"hahaha"; 10 context.value = @"hello world..."; 11 //遍歷Array數組元素,每一次傳入一個函數中進行處理 12 CFArrayApplyFunction((CFArrayRef)array, 13 CFRangeMake(0, CFArrayGetCount((CFArrayRef)array)), 14 MyContextCllbackFunc2, 15 &context);
C函數
1 static void MyContextCllbackFunc2(const void *user, void *context) { 2 RuntimeViewController *aUser = (__bridge RuntimeViewController *)(user); 3 MyConext *ctx = context; 4 NSLog(@"aUser.name = %@", aUser.name); 5 NSLog(@"ctx = %p", ctx); 6 }
輸出
2016-07-13 16:07:29.557 LJFSourceCodeLearn[5521:241563] aUser.name = 000 2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] ctx = 0x7fff51eb8bd8 2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] aUser.name = 111 2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] ctx = 0x7fff51eb8bd8 2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] aUser.name = 222 2016-07-13 16:07:29.558 LJFSourceCodeLearn[5521:241563] ctx = 0x7fff51eb8bd8
__unsafe_unretained 修飾符
ARC 修飾符 相關知識點而unsafe_unretained是跟weak類似的用法,但是__unsafe_unretained會更易造成野指針,
並且需要注意的是,盡管ARC式的內存管理是編譯器的工作,但是附有_unsafe_unretained修飾符的變量不屬於編譯器的內存管理對象
kCFNull
Nil NSNull NULL kCFNull
kCFNull,其宏定義,其實就是NSNull的單例
1 const CFNullRef kCFNull; // the singleton null instance
測試下
1 NSNull *null1 = (id)kCFNull; 2 NSNull *null2 = [NSNull null];
輸出信息
1 NSNull *) null1 = 0x0000000107b10af0 2 (NSNull *) null2 = 0x0000000107b10af0
其實 kCFNull這個宏 === [NSNull null]這一句代碼
isnan 和 isinf 函數
math.h 文件中定義isnan(x) not a number 此宏返回一個非零值;isinf(x) 當x是正無窮是返回1,當x是負無窮時返回-1。
附錄:iOS 常用數學函數