引子
已經很久沒有看過技術方面的書了,這次難得看了同事推薦的這本《Effective Objective-C 2.0》,適合已經會一點iOS的同學看,我記得我剛踏入這行時買的第一本書是《iOS開發指南》,基本沒看過,所以也不好說那本書寫的怎么樣。下面我先整理一下知識點,最后再說一下心得體會。
一、多用類型常量,少用#define預處理指令
- 某個類內的變量。比如:
#define ANIMATION_DURATION 0.3
可以改寫成
static const NSTimeInterval kAnimationDuration 0.3;
用這種方式的好處是描述了常量的含義,由此知道該常量類型(為NSTimeInterval類型),方便閱讀。注意:若常量為某個類內使用,則需要在前面加字母k。
- 2.公開某個常量(一般用于通知),可以這樣定義:
//In the header file
extern NSString *const EOCStringConstantNotification;
//In the implemention file
NSString *const EOCStringConstantNotification = @"EOCStringConstantNotification";
即:在.h文件中聲明,在.m實現文件中定義
二、在類繼承體系中查詢類型信息
- isKindOfClass:能夠判斷出對象是否為某個特定的實例
- isKindOfClass:能夠判斷出對象是否為某類或其派生類的實例。例如:
//A、B是兩個自定義的類 其中:B繼承自A(A是父類,B是子類)
[A isMemberOfClass:[A_Object class]];///YES
[A isMemberOfClass:[B_Object class]];///NO
[A isKindOfClass:[A_Object class]];///YES
[A isKindOfClass:[B_Object class]];///YES
[B isMemberOfClass:[A_Object class]];///NO
[B isMemberOfClass:[B_Object class]];///YES
[B isKindOfClass:[A_Object class]];///YES
[B isKindOfClass:[B_Object class]];///YES
注意:書中用了字典和可變字典來舉例子,其實是不準確的。真實打印結果發現如圖所示:
原因是因為其實每個Object-C對象實例都是指向某塊內存數據的指針。而描述Object-C對象所用的數據接口定義在一個結構體里,此結構體內存放類的“元數據”,比如類的實例方法,實例變量之類的,其中首個變量是isa指針,它定義了類對象所屬的類型(也就是isa指針所指向的類型)。super_class指針,定義了本類的超類。而打印dict的isa可以看到,其實本類實例類型其實是“__NSDictionaryM”,并非“NSMutableDictionary”,所以才返回NO,驗證isMemberOfClass和isKindOfClass的異同,最好自定義兩個有繼承關系的類
三、以弱引用避免保留環
避免保留環的最佳方式就是弱引用。這種引用經常用來表示“非擁有關系”。降屬性聲明為unsafe_unretained即可。另外weak屬性特質與unsafe_unretained的作用完全相同。但weak與unsafe_unretained的區別如下圖所示:
weak與unsafe_unretained
當指向EOCClassA實例的引用移除后,unsafe_unretained屬性仍然指向那個已經回收的實例,而weak屬性則指向nil。
四、以“自動釋放池塊”降低內存峰值
- 考慮下面這段代碼:
for (int i = 0; i < 100000; i++) {
[self doSomethingWithInt:i];
}
如果
doSomethingWithInt:
方法要創建臨時對象,那么這些對象很可能會放在自動釋放池里。即便這些對象在調用完方法之后就不再使用了,他們也依然處于存活狀態,因為目前還在自動釋放池里,等待系統稍后將其釋放并回收。然而,自動釋放池也要等線程執行下一次事件循環時才會清空。這就意味著在執行for循環時,會持續有新對象創建出來,并加入自動釋放池中,所有這種對象都要等for循環執行完才會釋放。這樣一來,在執行for循環時,應用程序所占內存量就會持續上漲,而等到所有臨時對象都釋放后,內存用量又會突然下降。
比方說,要從數據庫中讀出許多對象。代碼可能會這么寫:
NSArray *datebaseRecords = /*...*/;
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *recode in datebaseRecords) {
EOCPerson *person = [[EOCPerson alloc]initWithRecord:recode];
[people addObject:person];
}
則內存中會有很多不必要的臨時對象,他們本來應該提早回收的,增加一個自動釋放池可以解決此問題。如果把循環內的代碼包裹在“自動釋放池塊”中,那么在循環中自動釋放的對象就會在這個池,而不是線程的主池里面。解決方案如下:
NSArray *datebaseRecords = /*...*/;
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *recode in datebaseRecords) {
@autoreleasepool {
EOCPerson *person = [[EOCPerson alloc]initWithRecord:recode];
[people addObject:person];
}
}
五、遍歷
5.1、使用NSEnumerator來遍歷
//遍歷數組,可以這樣寫代碼
NSArray *anArray = /* ... */;
NSEnumerator *enumerator = [anArray objectEnumerator];
id object;
while ((object = [enumerator nextObject]) != nil) {
//Do something with 'object'
}
//遍歷字典,可以這樣寫代碼
NSDictionary *anDictionary = /* ... */;
NSEnumerator *enumerator = [anDictionary keyEnumerator];
id key;
while ((key = [enumerator nextObject]) != nil) {
id value = anDictionary[key];
//Do something with 'key' and 'object'
}
- 這種遍歷的好處:
- 1.不論遍歷哪種集合,都可以采用這套類似的語法。
- 2.使用NSEnumerator有多種“遍歷器”(enumerator)可供使用,比如反向遍歷數組所用的枚舉器,
NSEnumerator *enumerator = [anArray reverseObjectEnumerator];
這樣讀起來更順暢。
5.2、使用for..in 進行遍歷(也可以反向遍歷)
NSArray *anArray = /* ... */;
for (id object in [anArray reverseObjectEnumerator]) {
//Do something with 'object'
}
5.3、基于塊的遍歷方式(本身就通過GCD來并發執行遍歷操作)
//遍歷數組
NSArray *anArray = /* ... */;
[anArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//Do something with 'obj'
if (shouldStop) {
*stop = YES;
}
}];
//遍歷字典 可以根據條件終止遍歷
NSDictionary *anDictionary = /* ... */;
[anDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//Do something with 'key' and 'obj'
if (shouldStop) {
*stop = YES;
}
}];
- 注意:此遍歷方法也可以反向遍歷
- (void)enumerateObjectsUsingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block;
尾巴
書中還有一些GCD、內存管理,類簇等相關的知識,但本人才疏學淺,不敢吹牛放炮瞎胡造。其實上面的一些東西也是簡單的知識點展示,具體為啥這么用而不那么用那、這么寫好而那么寫不好,還要看書學習,這次權當是為了做一次簡單的知識總結,還有一點心得是:“常立志不如立長志”。多么樸素的道理啊!自己總是既想學這個又想學那個,而且這還不是最糟糕的,最糟糕的是這幾天想學學這個,那幾天又想學學那個,媽蛋的,總是不踏實,希望自己可以踏下心來,鉆研一些東西,下面這句話是中學老師在我改錯本后面寫的一句話,與君共勉:
平常心一個,不平成就得,更勝有心德!