-git
這節課講的內容很是之多,幾乎全都是 Objective-C 的內容,所以也會須要些時間來理解。
內容以下:
1. property
2. strong vs weak
3. nil
4. BOOL
5. Instance vs. Class Methods
6. Instantiation
7. Dynamic Binding
8. Introspection
9. Foundation Framework
10. Enumeration
11. Property List數據庫
第一個緣由也是首要緣由是,對於實例變量來講它提供了安全性和子類可訪問性。
若是我容許某人可以繼承咱們,那若是他們想要弄亂咱們的實例變量的話,咱們但願可以參與進來。所以若是他們把咱們的實例變量設成了其餘的東西,咱們可能想要彈回檢查下,確保他不會破壞它們。segmentfault
第二個緣由是,property 同時爲延遲實例化,UI 更新,一致性檢測(例如 speed < 1)等提供「值」。
這個「值」相似於一個閥門值,經過它咱們能夠進行延遲實例化,UI 更新。若是你經過 setter 改變了屏幕上某些對象的屬性,它會很方便的標記那些須要重畫的視圖。
一致性檢測。在第一節課的時候在 setter 中寫過確保速度不會超過光速的代碼,property 有許多咱們須要用到的有價值的東西,所以咱們不要直接訪問實例變量,而是儘可能使用 property。數組
實例變量安全
Q:@property 非要有一個實例變量嗎?
A:很顯然不是非得要有一個實例變量來輔助 @property(不要使用 @synthesize,並本身建立 setter 和 getter),若是你不想寫輔助實例變量,那你就不得不本身動手製造或計算 property,而不是存儲。
Q:是否我能夠擁有一個不帶 property 的實例變量?
A:固然能夠。但目前來講仍是用 property 好些。app
爲何要用點號ide
由於用起來很是漂亮好看。self.display.text 等同於 [self.display.text ...],若是你用方括號的話那你就得在一行代碼裏用一堆的方括號來請求方法,這很是恐怖,反之 self.display.text 就很是簡潔。
同時也使得 property 的讀取更明顯一點,讀代碼的時候更容易發現這是在調用 getter。
點號還能協助 C 代碼的結構體的讀取。函數
strong 和 weak 是指針的兩個屬性,它們都是針對對象指針的,不是任意內存裏其餘的指針。工具
strong 指針的意思是你想保留用 property/variable 引用的這個對象。只要你(或其餘的對象)用 strong 指針指向它,那麼編譯器就會留意這個 property 指定的對象,防止它被摧毀。只有當你把 property 設爲 nil 的時候對象纔會被摧毀(除非一個或多個其餘的對象對它仍然有 strong 引用)。測試
比較之,使用 weak 引用的話意味着你不想本身控制對象的生命週期。你用 weak 引用的對象只有在至少一個其餘對象具備 strong 引用時纔會保留。一旦沒有 strong 引用了,這個對象將會被摧毀而且你的weak property 也會自動設爲 nil。
iOS 中常常引用 weak 的狀況是:
1. delegate properties,它們常用 weak 引用以免保留循環。
2. subviews/一個試圖控制器的主視圖控件,由於那些試圖早就被主視圖強制引用了。
這是否是垃圾回收而是引用計數機制,計算內存中還有多少 strong 指針,當值爲0時就表示沒有 strong 指針了,那就立刻釋放掉。不像垃圾回收,你永遠不知道何時纔會釋放。垃圾回收是不可控的,而這是徹底可控的,當失去最後一個 strong 指針時,它會立刻被釋放掉,絕不遲疑。
nil 就是0,表示一個對象指針再也不指向任何東西的。全部 synthesize 生成的實例變量初始值都是 nil。
若是你但願指針指向某些東西,你能夠調用 setter 或在 getter 裏使用延遲實例化。
能夠在一個 if
語句中隱含地測試是否爲 nil。
if (obj) { } // 若是 obj 不爲 nil 那麼花括號裏的代碼將會執行
向 nil 發送消息不會使程序崩潰,事實上什麼都不會執行。若是有返回值,那就返回0。
除非當你有一個返回 C 結構體的方法時而且你把它發送給一個 nil 對象,那麼你會獲得一個未定義的 C 結構體。這種狀況要單獨處理
就是 YES 和 NO,不用 True 和 False。
注意:BOOL 必須大寫。
注意 dropBomb:at:from: 這個方法有一個對象參數 Bomb *,還有一個參數 CGPoint 沒有 *,因此這個指針要被傳入棧中,而 Bomb * 在堆上,幾乎全部的 objc 都在堆上。
實例方法以 -
開頭,它的對象是普通的實例對象。
類方法以 +
開頭,它的對象是類而不是實例,一般用來建立對象或者工具方法。
方法表達式
兩者的調用方法類似但不同,都是以 [
開頭。但實例方法中後面跟的是一個實例指針,而後再發送消息。但類方法在 [
後面是一個類。有兩種方法表示這裏的類,簡單點就是直接寫類名,另外一個就是發送一個叫作 class
的消息給實例,這個特殊方法會返回這個實例的類,而後就能夠給這個類發消息了。
在實例方法裏,根據繼承原理髮消息給 self 或者 super 其實都是發給 self。
在類方法裏,self 是其餘類的類方法,在類方法中給 self 發消息只能發類方法。由於 self 不是實例,只是個類,super 也同樣。
Q:何時用到實例方法呢?
A:幾乎一直在用。Q;何時用到類方法呢?
A:當要建立一個實例時,或者獲取一個共享的實例,或者獲取關於類的一些公共信息時。Q:類能夠具備 property 或者實例變量嗎?
A:不能。類不是實例,因此無法保存任何東西。
獲取對象的最好方法是要求其餘對象給你一個。
NSString’s - (NSString *)stringByAppendingString:(NSString *)otherString; NSString’s & NSArray’s - (id)mutableCopy; NSArray’s - (NSString *)componentsJoinedByString:(NSString *)separator;
前幾課用過 stringByAppendingString
。NSString
和 NSArray
有個 mutableCopy
方法會返回一個可變的對象。還有更復雜的 componentsJoinedByString
方法,輸入一個分隔符好比逗號或空格獲得由數組元素鏈接成的字符串。
不是全部返回的對象都是新建的,若是對象已經存在就返回它的指針
NSArray’s - (id)lastObject; NSArray’s - (id)objectAtIndex:(int)index;
好比 lastObject
在數組中獲取最後一個元素,它不會新建一個元素,只會返回一個指向最後元素的指針。全部數組中的元素都是 strong
指針,因此除非數組釋放或者移除該元素,不然它一直都是被 strong
指針所指向。全部相似數組、字典的類都是這樣的。
獲取對象的主要方法仍是使用類方法
NSString’s + (id)stringWithFormat:(NSString *)format, ... UIButton’s + (id)buttonWithType:(UIButtonType)buttonType; NSMutableArray’s + (id)arrayWithCapacity:(int)count; NSArray’s + (id)arrayWithObject:(id)anObject;
若果你要用代碼生成一個按鍵,你能夠發消息給 UIButton
類新建一個按鍵。因此絕大多數對象都是經過其餘對象或者類來獲取的。
從頭分配和初始化一個對象
NSMutableArray *stack = [[NSMutableArray alloc] init]; CalculatorBrain *brain = [[CalculatorBrain alloc] init];
方法是使用 alloc
和 init
的組合。alloc
是 NSObject
的類方法,爲對象在堆上分配一個足夠大的空間,分配的對象初始值是 nil
或者0。不少對象只分配爲 nil
還不夠,還須要初始化。無論對象是否須要初始化,咱們老是要在 alloc
外面加上 init
或者 initWith
。因此永遠都要這麼寫,決不能只是 alloc
甚至也不能下一行再 init
,必定要加在後面。
Alloc and init
alloc
由 NSObject
完成,並且絕大多數的類有無參的 init
方法。官方文檔不容許你的類沒有 init
初始方法,因此你必須設計可以被 alloc
和 init
的類。可是你能夠有其餘任意個 init
方法。
更復雜的 init 方法
- (id)initWithFrame:(CGRect)aRect; // UIView的初始化代碼 UIView *myView = [[UIView alloc] initWithFrame:thePerfectFrame];
UIView
它有一個 init
方法叫作 initWithFrame
,這個方法容許初始化爲一個矩形,還能夠只用 init
,這會初始化大小和座標都爲(0,0),但能夠稍後再設置座標大小。因此它有兩種初始化,initWithFrame
和普通的 init
。
帶有不一樣參數的各類初始化示例
- (id)initWithCharacters:(const unichar *)characters length:(int)length; - (id)initWithFormat:(NSString *)format, ...; - (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;
NSString
擁有超過十幾種初始化,stringWithFormat
等同於 [self alloc]initWithFormat
,而 initWithData
能夠把各類編碼的數據轉換成字符串。
類必須爲子類指定一個初始化
全部的類必須爲子類提供一個初始化方法,方便子類初始化的時候先初始化父類。由於你建立子類的時候會調用子類的初始化,當你這麼作的時候須要用這個 init
方法先初始化父類。知道這個指定的 init
方法很重要,好比 UIView
的初始化方法是 initWithFrame
,因此若是我繼承了 UIView
,那麼我須要在初始化裏寫 [super initWithFrame]
,不然父類就無法初始化了。不要調用其餘 init
方法,只要指定的那個(能夠經過查看文檔找到父類指定的 init
方法),而後在子類的初始化裏調用這個方法。
靜態類型的初始化
因爲繼承緣由,init
方法應該是 id
返回類型(不是靜態類型)。
可是,若是你有一個本地變量在經過初始化賦值,那麼就應該是固定類型的。
例如,MyObject *obj = [[MyObject alloc] init];
這裏的 obj
,我沒寫 id obj
即便 init
返回的是 id
,而是寫成寫成 MyObject
,這樣編譯器就會確保不會發送錯誤內容。
Q:爲何要有一個指定的初始化方法?
A:若是仔細想一下,這樣能夠避免變成一個循環。我有一個本身的 init (A)
調用父類的 init (B)
,而後父類的 init (B)
又調用本身其餘某個 init (C)
,由於 B 與 C 之間想要共享代碼。而那個 init (C)
又去調用本身的 init (D)
,init (D) 可能已經被你重寫,這樣就會致使 D 又去調用 B。也就是 A→B→C→D→B→C...
,這就無限循環了。因此經過指定 init
,全部你本身的 init
就會請求指定的 init
,而後一直向上請求父類指定的 init
,而後一直這樣向上。
新建自定義的初始化方法
@implementation MyObject - (id)init { self = [super init]; // 使用父類指定的初始化 if (self) { // 在這裏初始化子類 } return self; } @end
初始化是很是廣泛的作法,但在 objc 中不經常使用。由於有了 property
,因此能夠延遲實例化,這樣一來就不用初始化了。
還記得計算器程序的 Model 嗎,咱們的 viewController
就沒有初始化。若是你確實須要,好比初始化一些狀態,上面的代碼就是演示。這是 MyObject
指定的初始化 init
,這裏有行很特別的代碼 self = [super init]
。某些人可能看到 self =
就崩潰了,以爲 self
不能被賦值。在 objc 中 self
只是本地指針,但這是惟一能夠對它賦值的狀況。對 self
賦值這是一種協議機制,確保 super
的初始化在咱們的以前,若是 super
沒有初始化就返回 nil
。因此若是由於某些緣由無法初始化,好比依賴的數據庫掛了,就容許返回 nil
。因此在子類裏要檢查父類是否返回 nil
。這裏個人 init
調用了 super
指定的初始化,檢查它是否返回 nil
。
示例:CalculatorBrain 子類的快捷初始化
@implementation CalculatorBrain - (id)initWithValidOperations:(NSArray *)anArray { self = [self init]; self.validOperations = anArray; // will do nothing if `self == nil` return self; } @end
這裏我讓個人 CalculatorBrain 只能被合法的運算符初始化。注意這個 init
裏面我沒有調用 super
的指定初始化,我在本身的快捷初始化裏調用了本身的指定初始化。這就是全部其餘的初始化都會調用指定的初始化,而後再向上調用 super
的指定初始化。
指在運行時根據操做對象的數據類型的不一樣來選擇合適的過程(方法)。它能夠提升程序的靈活性。它是面向對象的必要條件之一。在Ruby中變量是沒有類型的,所以必然能夠進行動態綁定。
全部對象都分配在堆中,所以你須要使用一個指針
NSString *s = ...; // 靜態類型 id obj = s; // 不是靜態類型,但徹底合法 Never use 「id *」 (that would mean 「a pointer to a pointer to an object」).
執行消息發送的決定代碼發生在 runtime
你能夠想一下,[obj* method]
在編譯器裏是個 C 函數,咱們叫它 OBJC message sent。OBJC message sent 第一個參數是一個對象指針,第二個參數是一個基本信息描述符,而後剩下的參數就跟你方法中的參數一致。想象一下這個過程,在運行過程當中每次發消息都會調用這個函數。這個函數作的第一件事是查看這是個什麼類,而後找到類的方法。若是這個類和他的父類都沒有這個方法的實現,程序就崩潰了。因此這個過程當中,無所謂類型是 id 或者 NSString 什麼的,這個函數會本身去代碼中查找,因此給出具體類型其實只是爲了方便編譯器查找 bug。
映射一個指針是合法的,有時甚至鼓勵這麼作
id obj = ...; NSString *s = (NSString *)obj; // 這麼作很危險,最好你明白本身在作什麼
可是一般咱們只在使用內省的時候才這麼作。
NSObject
- (NSString *)description is a useful method to override (it’s %@ in NSLog()). - (id)copy; // not all objects implement mechanism (raises exception if not) - (id)mutableCopy; // not all objects implement mechanism (raises exception if not)
foundation 是個 framework,含有不少重要對象。
NSObject
幾乎是任何 iOS 對象的基礎,它沒有過多內容。它有 +alloc
類方法,還有那些內省方法,還有 description 來建立一個對象的 description,這樣咱們可使用 NSLog 輸出到控制檯。一般咱們會重載 description 來顯示本身的內容。
- (id)copy;
和 - (id)mutableCopy;
。有些人會很興奮,覺得能夠直接複製對象了。但實際上你不能夠,只有實現了特殊協議的對象才能夠。幸運的是 foundation 中大多數對象均可以,若是你要知道一個對象可否 copy,就去查它的文檔。咱們還沒講協議,遲些會講到。可是若是你看到一個對象有實現協議,那就能夠 copy。
Q:這是不是深度複製?好比複製一個數組會不會複製數組裏的東西。
A:不是。這是淺複製,某些對象是深度的,但這個是淺的。
NSString
char *
。 self.display.text = [self.display.text stringByAppendingString:digit]; self.display.text=[NSStringstringWithFormat:@「%g」,brain.operand]; // 類方法
NSMutableString
NSMutableString *ms = [[NSMutableString alloc] initWithString:@「0.」]; NSMutableString *ms = [NSMutableString stringWithString:@「0.」]; // 繼承自 NSString [ms appendString:digit];
可變版本的字符串叫作 NSMutableString
,它的部分功能用 NSString
也能實現,因此咱們能夠利用 append 來實現可變。
Q:爲何咱們不鼓勵用 NSMutableString
?它這麼好用。
A:記住,若是你經過 append 新建一個字符串,過程和用 NSMutableString
是同樣的。此外,不可變的 NSString
已經被優化到了極致。因此咱們極少用到 NSMutableString
。一般我就是想用不可變的 NSString
發消息來得到一個新的字符串。
NSNumber
NSNumber *num = [NSNumber numberWithInt:36]; float f = [num floatValue]; // 返回 36 做爲 float (i.e. will convert types)
NSNumber
,原始類型的包裝。它還提供了轉換功能,好比你包裝了 int 但須要一個 float,它會自動轉換好給你,反之亦然。
NSValue
CGPoint point = CGPointMake(25.0, 15.0); // CGPoint is a C struct NSValue *pointObject = [NSValue valueWithCGPoint:point];
NSValue
,爲結構體之類的數據提供包裝,好比這裏的 CGPoint
。NSValue
就是爲這些數值提供包裝的。好比能夠用它把一些座標點裝進數組。
NSData
NSData 是用來裝無結構數據的,二進制碼之類。這些數據的意義依賴於傳遞它們的 API,多是字符串、圖片等。
NSDate
NSDate,用來找到當前時間或者存儲過去或將來的時間和日期。此外還有各類日期時間的格式化。例如,NSCalendar
,NSDateFormatter
,NSDateComponents
。
NSArray
+ (id)arrayWithObjects:(id)firstObject, ...; // nil 爲結束參數 NSArray *primaryColors = [NSArray arrayWithObjects:@「red」, @「yellow」, @「blue」, nil]; + (id)arrayWithObject:(id)soleObjectInTheArray; // 比你想的更有用 - (int)count; - (id)objectAtIndex:(int)index; - (id)lastObject; // 若是數組沒有對象就返回 nil - (NSArray *)sortedArrayUsingSelector:(SEL)aSelector; - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)selectorArgument; - (NSString *)componentsJoinedByString:(NSString *)separator; - (BOOL)containsObject:(id)anObject; // could be slow, think about NSOrderedSet
NSArray,多是 NSString 以後第二受歡迎的對象。它是一個有序的對象集合,根據你增長和移除操做會變大和縮小。和其餘語言裏的數組概念是同樣。可是它是不可變的,有人會問你剛剛還說它會縮小,個人意思是建立的時候它會自動根據內容調整大小,以後就不變了。
Q:想一想你在不一樣對象之間經過 API 傳遞對象,你傳了一個可變的過去,好比可變數組。過一下子你在這個數組裏加了些東西,那麼以前傳過去的會被修改嗎?
A:這種狀況很難處理,他須要在傳進來的時候建一個可變數組的快照。這是個好理由讓你們都有可變數組,但只有一個是真的。事實上你會看到大多數的 NSString API 是@property(copy)
,這個還沒講過,但它就像是一個 strong 的可變副本。因此不可變纔是王道,無論是字符串仍是數組。
Q:那麼當你有了個不可變數組,怎麼往裏面放東西呢?
A:在建立的時候放。有很多的類方法能夠建立一個數組,這個 arrayWithObjects:
傳遞一串對象進來就生成了一個不可變數組。也有 arrayWithObject:
建立單個對象的數組,你會問爲何?你可能有 API 可以接受多個參數,但一般只接受一個。你只要一個數組,因此這個就用來只傳遞一個。
NSArray 的主要方法有 count
和 objectAtIndex:
。count
表示數組的個數。objectAtIndex:
表示指定位置的內容。
Q:能夠往數組裏放 nil 嗎?
A:不行。有一個對象叫作 NSNull,它能夠放在數組裏,可是 nil 不行。什麼是 NSNull ?它僅僅是個空白的佔位符。
對象的容器可能會運行很慢,特別是當數組很大的時候。由於它不知道怎麼在裏面查找,這時候就會用到 NSOrderedSet
或 NSSet
。
NSMutableArray
+ (id)arrayWithCapacity:(int)initialSpace; // initialSpace is a performance hint only + (id)array; - (void)addObject:(id)anObject; // at the end of the array - (void)insertObject:(id)anObject atIndex:(int)index; - (void)removeObjectAtIndex:(int)index; - (void)removeLastObject; - (id)copy; // returns an NSArray (i.e. immutable copy); and NSArray implements mutableCopy
NSMutableArray 是個可變的數組,你能夠用 NSArray 的方法建立,也能夠用 array 建個空的數組。不要被 arrayWithCapacity:
迷惑了,它仍是新建個空的數組。Capacity 只是個提示。
NSMutableArray 的主要方法是 add 或者 insert 還有 remove。別忘了 copy 之於 NSMutableArray 會返回不可變的數組,可是 NSArray 的可變 copy 會返回 NSMutableArray。還有別忘了 NSMutableArray 繼承了 NSArray 的所有方法。
NSDictionary
字典是一個不可變的哈希表,經過關鍵字來來查找對象以得到一個值。兩個重要的方法是 count
,關鍵字對象數目,和 objectForKey:
,經過 key 查找對象。keys 會發送給哈希表,並用 isEqual:
來尋找配對的內容,若是是指針就匹配指針地址。大多數時候你會用 NSString 來做爲 key,而後匹配字符。
NSMutableDictionary 用 set 來添加,remove 來移除。若是你有另外一個 NSMutableDictionary,還能夠整個合併過去。
NSSet
+ (id)setWithObjects:(id)firstObject, ...; + (id)setWithArray:(NSArray *)anArray; - (int)count; - (BOOL)containsObject:(id)anObject; - (id)anyObject; - (void)makeObjectsPerformSelector:(SEL)aSelector;
NSSet 是一個不可變的,無序的惟一的對象集合。惟一的表示你不能放兩個同樣的對象,不是說同一個對象指針,而是內容同樣的兩個對象。這是當你想要檢查成員時候用的,好比我是否已經擁有這個對象了。
Q:爲何是不可變的?
A:好比你有個顏色的 set,那就能夠查詢一個顏色是否是在 set 裏了。
count
和 containsObject:
是兩個關鍵方法。anyObject
表示隨意對象,當任意對象均可以作某個操做時很是有效。
NSMutableSet
- (void)addObject:(id)anObject; // does nothing if object that isEqual:anObject is already in - (void)removeObject:(id)anObject; - (void)unionSet:(NSSet *)otherSet; - (void)minusSet:(NSSet *)otherSet; - (void)intersectSet:(NSSet *)otherSet;
可變的 set 能夠添加和刪除元素,不能在指定位置插入由於它是無序的。
NSOrderedSet
- (int)indexOfObject:(id)anObject; - (id)objectAtIndex:(int)anIndex; - (id)firstObject; and - (id)lastObject; - (NSArray *)array; - (NSSet *)set;
NSOrderedSet 不是 NSSet 的子類,可是是 NSArray 和 NSSet 的混合物。它可以在指定位置插入對象,但仍然不能重複放入對象。NSOrderedSet 執行成員檢查時比 NSSet 和 NSArray 要快得多。
NSMutableOrderedSet
- (void)insertObject:(id)anObject atIndex:(int)anIndex; - (void)removeObject:(id)anObject; - (void)setObject:(id)anObject atIndex:(int)anIndex;
NSMutableOrderedSet 是可變的,其餘都和 NSOrderedSet 同樣。
以一個高效的方式遍歷集合的成員
例如:NSArray of NSString objects NSArray *myArray = ...; for (NSString *string in myArray) { // 編譯器不知道 myArray 的內容 double value = [string doubleValue]; // 若是字符串不是 NSString 程序會崩潰 } 例如:NSSet of id (NSArray of id 也很容易) NSSet *mySet = ...; for (id obj in mySet) { // do something with obj, but make sure you don’t send it a message it does not respond to if ([obj isKindOfClass:[NSString class]]) { // 發送 NSString 消息給對象 with impunity } }
和其餘語言同樣,你可使用 for-in
。好比在 myArray
裏枚舉出 string。若是 myArray
裏沒有 string,運行時會崩潰且不會有警告。由於編譯器不會知道 myArray
的內容。若是你的數組可能含有其餘對象,就能夠想這樣寫 id。注意,我在代碼裏作了內省,在發送 string 消息前檢測其是不是 string。
遍歷一個字典的關鍵字或值
例如: NSDictionary *myDictionary = ...; for (id key in myDictionary) { // do something with key here id value = [myDictionary objectForKey:key]; // do something with value here }
字典的枚舉是經過關鍵字。因此在循環裏能夠對 key 的值作一些操做,但不是修改。說到修改,在 for-in
裏面不要修改可變數組,好比添加成員之類的都不要作。
Q:爲何要有 Property List?
A:由於 iOS 有的 API 參數 id,它的文檔傷規定這個 id 必須是 Property List。這就意味着必須是這六個中的一個。
例如:NSUserDefaults
是個輕量級的 Property List 數據庫。輕量級就是小型的,內容很少的數組字典等,不是大型的圖片、數據之類的。它基於 NSDictionary,存在於應用程序啓動時的空隙。
[[NSUserDefaults standardUserDefaults] setArray:rvArray forKey:@「RecentlyViewed」]; Sample methods: - (void)setDouble:(double)aDouble forKey:(NSString *)key; - (NSInteger)integerForKey:(NSString *)key; // NSInteger is a typedef to 32 or 64 bit int - (void)setObject:(id)obj forKey:(NSString *)key; // obj must be a Property List - (NSArray *)arrayForKey:(NSString *)key; // will return nil if value for key is not NSArray Always remember to write the defaults out after each batch of changes! [[NSUserDefaults standardUserDefaults] synchronize];
使用 standardUserDefault
方法進行讀寫,這和字典很像,可是有更具體的操做。
Q:若是你使用了 setObject:forkey:
,那麼這裏的 obj 是什麼呢?
A:是 Property List。
arrayForKey:
,若是 key 裏面保存的不是 array 就返回 nil。最後一點,不管什麼時候你用了 NSUserDefaults,你必須和磁盤進行同步。這是爲了安全起見,只要寫 [[NSUserDefaults standardUserDefaults] synchronize];
就行。任何操做以後都要同步,不然數據不會保存。同步不是沒有開銷,只是很是少而已。因此每次都要同步,而不是積累一大堆東西以後再作。
-