第一條:了解Objective-C語言的起源
1.Objective-C語言的對象所占內存總是分配在“堆空間”中。
2.分配在堆中的內存必須直接管理,而分配在棧上用于保存變量的內存則會在其棧幀彈出時自動清理。
3.基本類型也被存放在棧空間。
第2條:在類的頭文件中盡量少引用其他頭文件
1.當一個類作為另一個類的屬性的類型時,只需聲明
@class className
有兩個好處:
(1) 減少編譯時間
(2)解決了兩個雷相互引用的問題。
如果在各自頭文件中引入對方的頭文件,則會導致“循環引用”。當解析其中一個頭文件時,編譯器會發現它引入了另一個頭文件,而那個頭文件回過頭來引用了第一個頭文件。使用#import而非#include指令雖然不會導致死循環,但這意味著兩個類里有一個無法被正確編譯。
2.最好把協議單獨放在一個頭文件中。
3.委托協議就不能單獨寫一個頭文件了。此時最好能在實現文件中聲明此類實現了委托協議,并把這段代碼放到“class-continuation”分類里。這樣的話,只要在實現文件中引入包含委托協議的頭文件即可。
第3條:多用字面量語法,少用與之等價的方法
1.字面量字符串
NSString *someString = @"Effective Objective-C 2.0";
2.字面數值
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159
NSNumber *boolNumber = @YES;
NSNumber *charNmuber = @'a';
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);
3.字面量數組
NSArray *animals = @[@"cat",@"dog",@"mouse",@"badger"];
NSString *dog = animals[1];
4.字面量字典
NSDictionary *personData = @{@"firstName":@"Matt",@"lastName":@"Galloway",@"age":@28};
NSString *lastName = personData[@"lastName"];
與數組一樣,用字面量語法創建字典時也有個問題,那就是一旦有值為nil,便會拋出異常
5.可變數組與字典
mutableArray[1] = @"dog";
mutableDictionary[@"lastName"] = @"Galloway";
使用字面量語法創建出來的字符串、數組、字典對象都是不可變的。若想要可變版本的對象,則需復制一份
NSMutableArray *mutable = [@[@1,@2,@3,@4,@5] mutableCopy];
第4條:多用類型變量,少用#define預處理指令
1.用以下方式定義的變量包含類型信息,其好處是清楚地描述了常量的含義。
static const NSTimeInterval kAnimationDuration = 0.3;
如果試圖修改由const修飾符所聲明的變量,那么編譯器就會報錯。而static修飾符則意味著改變量僅在定義此變量的編譯單元中可見(.m文件中)。
2.如果需要對外公開某個變量,應該這樣定義:
// In the header file
extern NSString *const EOCStringConstant;
// In the implementation file
NSString *const EOCStringConstant = @"VALUE";
這樣定義常量優于使用#define預處理指令,因為編譯器會確保常量值不變。一旦在.m中定義好,即可隨處使用。而采用預處理指定所定義的常量可能會無意中遭人修改,從而導致應用程序各個部分所使用的值互不相同。
第5條:用枚舉表示狀態、選項、狀態碼
1.枚舉只是一種常量命名方式,某個對象所經歷的各種狀態就可以定義為一個簡單的枚舉集。
enum EOConnectionState {
EOConnectionStateDisconnected,
EOConnectionStateConnecting,
EOConnectionStateConnected,
};
// 定義變量的方式
enum EOConnectionState state = EOConnectionStateDisconnected,
若是每次不用敲入enum而只需寫EOConnectionState就好了。要想這樣做,則需使用typedef關鍵字重新定義枚舉類型:
enum EOConnectionState {
EOConnectionStateDisconnected,
EOConnectionStateConnecting,
EOConnectionStateConnected,
};
typedef enum EOConnectionState EOConnectionState;
// 定義變量的方式
enum EOConnectionState state = EOConnectionStateDisconnected,
2.可以指明用何種“底層數據類型”來保存枚舉類型的變量。這樣做的好處是,可以向前生命枚舉變量了。若不指定底層數據類型,則無法向前聲明枚舉類型,因為編譯器不清楚底層數據類型的大小,所以在用到此類枚舉時,也就不知道究竟該給變量分配多少空間。
指定底層數據類型所用的語法是:
enum EOCConectionStateConnectionState :NSInteger { /*...*/};
在向前聲明時指定底層數據類型:
enum EOCConectionStateConnectionState :NSInteger;
3.不使用編譯器所分配的序號,而是手工指定某個枚舉成員所對應的值。
enum EOConnectionState {
EOConnectionStateDisconnected = 1,
EOConnectionStateConnecting, // 2
EOConnectionStateConnected, // 3
};
4.定義選項的時候,若這些選項可以彼此組合,各選項可通過“按位或操作符”來組合。
enum UIViewAutoresizing {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
因為每個枚舉值所對應的二進制表示中,只有一個二進制位的值是1。用“按位與操作符”即可判斷出是否已啟用某個選項:
enum UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if(resizing & UIViewAutoresizingFlexibleWidth){
// UIViewAutoresizingFlexibleWidth is set
}
5.凡是需要按位或操作來組合的枚舉都應使用NS_OPTIONS定義。若是枚舉不需要相互組合,則應使用NS_ENUM來定義。
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
};
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
以上代碼等價于
6.枚舉用在switch語句里,最好不要有default分支。
第6條:理解屬性這一概念
1.在OC中,把實例變量當做一種存儲偏移量所用的“特殊變量”,交由“類對象”保管。偏移量會在運行期查找,如果類的定義變了,那么存儲的偏移量也就變了,這樣的話,無論何時訪問實例變量,總能使用正確的偏移量。
2.@synthesize和@dynamic
@implementation EOCPerson
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
上述語法會將生成的實例變量名為為_myFirstName與_myLastName,而不再使用默認的名字。
使用@dynamic關鍵字,它會告訴編譯器:不要自動創建實現屬性所用的實例變量,也不要為其創建存取方法。而且,在編譯訪問屬性的代碼時,即使編譯器發現沒有定義存取方法,也不會報錯,它相信這些方法能在運行期找到。
3.具備readonly屬性的屬性僅擁有獲取方法,只有當改屬性由@synthesize實現時,編譯器才會為其合成獲取方法。你可以用此特質把某個屬性對外公開為只讀屬性,然后在“class-continuation分類”中將其重新定義為讀寫屬性。
4.內存管理語義
- assign “設置方法”只會執行針對“純量類型”(scalar type,例如CGFloat或NSInteger等)的簡單賦值操作。
- strong 此特質表明該屬性定義了一種“擁有關系”。為這種屬性設置新值時,設置方法會先保留新值,并釋放舊值,然后再將新值設置上去。
- weak 此特質表明該屬性定義了一種“非擁有關系”。為這種屬性賦新值時,設置方法既不保留新值,也不釋放舊值。此特質同assign類似,然而在屬性所指的對象遭到摧毀時,屬性值也會清空。
- unsafe_unretained 此特質的語義和assign相同,但是它適用于“對象類型”,該特質表達一種“非擁有關系”,當目標對象遭到摧毀時,屬性值不會自動清空,這一點與weak有區別。
- copy 此特質所表達的所屬關系與strong類似,然而設置方法并不保留新值,而是將其“拷貝”。只要實現屬性所用的對象是“可變的”,就應該在設置新屬性值時拷貝一份。
第7條:在對象內部盡量直接訪問實例變量
在寫入實例變量時,通過其設置方法來做,而在讀取實例變量時,則直接訪問之。此辦法既能提高讀取操作的速度,又能控制對屬性的寫入操作。