1.1-了解Objective-C
了解Objective-C 語言的起源
1. Objective-C(以下簡稱Oc)是在C語言的基礎上添加了面向對象特性。
Oc是C語言的超集(superset),因此C語言的所有功能特性都可以適用于Oc。
2. Oc是使用“消息結構”(messaging structure),而非常見的“函數調用”(function calling)。它們區別像這樣
// message structure
Object *obj = [Object new];
[obj performWith:para1 and:para2]
// 其特性就是“運行時組件(Runtime)”,其本質上就是一種與開發者所編代碼相鏈接的 “動態庫”(dynamic libary),其代碼能把開發者編寫的所有程序粘合起來。
// 運行時所執行的代碼由運行環境決定,動態特性明顯,但是有些問題編譯期間無法發現。
// 所有方法,都是運行時去查找,運行。接收消息的對象也要在運行時去查找。這時候就可能出問題。見后面。
// functions calling
Object *obj = new Object;
obj->perform(para1,para2);
// 與消息型相反,函數方法都有編譯器編譯的時候實現,可以預先發現一些潛在問題。
// 運行時所執行的代碼由編譯器決定;
// 如果是多態方法,運行時就會去“虛方法表(virtual table)”查找出具體哪一個函數;
3. Oc中的對象總是分配在“堆空間”(heap space),不會分配到“棧”(stack)上。
Oc 將堆內存管理抽象出來了,不需要用malloc 及free 來分配或釋放對象所占內存,Oc
運行期環境把這部分工作抽象成一套內存管理架構,叫 ”引用計數“ 。一個例子
NSString *someString = @"The string";
NSString *anotherString = someString;
4. 能用C的結構體,不用對象,結構體比對象更有效率。
對象要分配空間,釋放空間,而結構體不需要。結構體儲存在??臻g。
5. 非對象類型(nonobject type),分配在棧上,在其棧幀彈出時自動清理。
1.2-頭文件
核心點:在類的頭文件中盡量少引用其他頭文件
1. Oc 中編寫類方式與 C和C++一樣,使用頭文件(header file)、實現文件(implementation file)來區隔代碼。
2. 引用其他類,使用@class xxx的向前聲明方式(forward declaring)。
也可以使用#import或#include,但是不夠優雅,這里就要知道引用 類的具體細節,這里會引用到引用類的具體實現,會增加編譯時間。使用@clss 還可以減少兩個類之間的耦合。
3.應該將引入頭文件的時機盡量延后(放在實現文件),只有確有需要的時候才引用,這樣子可以減少類的使用者所需引用的頭文件數量??s短編譯時間。
可以使用@class時,首選@class。
只有在迫不得已的時候才用#import (如:繼承,實現協議)。
協議建議放在單獨的一個頭文件。避免引入協議時,引入頭文件中等其他內容。
使用@class 可以減少.h中對其他類的依賴、減少鏈接到其他類所需要的時間,從而降低編譯時間。
4. 兩個類互相引用時: A類中引用B類,B類中也引用A類。必須用@class,不然會出現循環引用。
用#import 而不用 #include
import可以避免重復引用
如果用#include的話,需要進行避免重復的宏定義
#ifndef HEADER
#define HEADER
xxx
#endif
1.3-字面量語法、常量、枚舉
一、盡量用字面量語法,便于理解
1. 字面數值
// 字面量語法
NSNumber *itemNo = @1;
// 傳統聲明
NSNumber *itemNo = [NSNumber numberWithInt:1];
2. 字面量數組
不能有nil值,nil值為結尾標示
NSArray *arr1 = @[@"1",@"2"];
NSArray *arr2 = [NSArray arrayWithObjects:@"1",@"2",nil]; // 注意nil結尾
3. 字面量字典
不能有nil值,nil值為結尾標示
NSDictionary *dic = @{@"key1":@"val1",
@"key2":@"val2"};
4. 可變數組和字典
字面創建的都是不可變類型,如果想創建可變類型,需要mutableCopy
NSMutableDictionary *dic = [@{@"key1":@"val1",
@"key2":@"val2"} mutableCopy];
5. 字符串字面量創建的是常量,對象不在持有了也不會立馬被釋放
// Oc會做字符串的編譯單元,而且會合并相同字符串的編譯單元,來減少額外的消耗去鏈接這些編譯單元。
NSString str1 = @“i am yun”;
NSString str2 = @“i am yun”;
// 此時,str1跟str2內存地址是一樣的。
// 字符串常量創建后,不再修改。即使引用它的對象不再指向它,字符串常量也不會立即施放。
二、 多用類型常量,少用預處理
1. 不要用預處理命令定義常量,用靜態常量代替
預處理命令定義的,不含有類型信息
2. 類內使用的常量,定義在實現文件,可用k做前綴
// in the implementation file
static const int kTimeItv = 1;
3. 如果需要其他類引用常量,在接口用extern定義,在實現文件實現,可用類名做前綴,如通知鍵值
// in the interface
extern const NSString *notiKey;
// in the implementation file
const NSString *notiKey = @"notiKey";
4. 編譯器會在 “數據段”(data section)為字符串分配存儲空間,這里在上面C 語言的內存模型有講,數據段通常是指用來存放程序中已初始化的全局變量的一塊內存區域。數據段屬于靜態內存分配。
三、 用枚舉表示狀態、選項、狀態碼
1. 用宏來定義枚舉類型
這些宏具備向后兼容(backward compatibility)能力,如果目標平臺編譯器支持新標準,那就使用新式語法,否則改用舊式語法。
- NS_ENUM宏 定義通用枚舉
typedef NS_ENUM(NSInteger, NSWritingDirection) {
NSWritingDirectionNatural = -1, //值為-1
NSWritingDirectionLeftToRight = 0, //值為0
NSWritingDirectionRightToLeft = 1 //值為1
};
- NS_OPTIONS宏 定義位移枚舉
typedef NS_OPTIONS(NSUInteger, UISwipeGestureRecognizerDirection) {
UISwipeGestureRecognizerDirectionNone = 0, //值為0
UISwipeGestureRecognizerDirectionRight = 1 << 0, //值為2的0次方
UISwipeGestureRecognizerDirectionLeft = 1 << 1, //值為2的1次方
UISwipeGestureRecognizerDirectionUp = 1 << 2, //值為2的2次方
UISwipeGestureRecognizerDirectionDown = 1 << 3 //值為2的3次方
};
2. 在switch 語句中,最好不要有default 分支,這樣子要做到處理所有樣式,這樣子在新家類型的時候,沒有default 編譯器會發出警告,讓我們注意到。
3. 實現枚舉所用的數據類型取決于編譯器,不過其二進制位(bit)的個數必須能完全表示下枚舉編號才行,一個字節含8個二進制位,所以至多能表示256(28)個枚舉變量。