--- title: 'Objective-C Foundation 基礎' disqus: kyleAlien --- Objective-C Foundation 基礎 === ## Overview of Content Objective-C 程式中最常用到的是 Foundation、ApplicationKit 兩個架構,而 Foundation 架構的所有類別都繼承自 ONObject 物件 [TOC] ## 數字物件 - NSNumber Foundation 架構提供 **NSNumber** 對幾種資料類型再次進行封裝;數字物件的類型如下表 | 基礎類型 | Foundation 封裝後的類型 | | - | - | | char | `NSNumber` | | UnsugnedChar | `NSNumber` | | Short | `NSNumber` | | UnisgnedShort | `NSNumber` | | Integer | `NSNumber` | | UnisgnedInteger | `NSNumber` | | int | `NSNumber` | | UnisgnedInt | `NSNumber` | | Long | `NSNumber` 或者 `NSDecimalNumber` | | UnisgnedLong | `NSNumber` 或者 `NSDecimalNumber` | | LongLong | `NSNumber` 或者 `NSDecimalNumber` | | UnisgnedLongLong | `NSNumber` 或者 `NSDecimalNumber` | | float | `NSNumber` 或者 `NSDecimalNumber` | | double | `NSNumber` 或者 `NSDecimalNumber` | | Bool | `NSNumber` 或者 `NSDecimalNumber` | :::success * `NSDecimalNumber` 是用於處理高精度計算的類型… 它使用 BCD(`Binary-Coded Decimal`)表示數字,因此比起 `NSNumber` 具有較高的精度 ::: ### NSNumber 初始化 1. **類方法 `numberWith<類型名稱>`**:數字物件的建立、初始化 (建立同時初始化) 格式如下 ```objectivec= NSNumber *物件名 = [NSNumber numberWith<類型名稱>]; ``` 範例: ```objectivec= #import <Foundation/Foundation.h> void number_1(void) { NSNumber *boolInstance = [NSNumber numberWithBool: YES]; NSNumber *shortInstance = [NSNumber numberWithShort: 1]; NSNumber *longInstance = [NSNumber numberWithLong: 100L]; NSNumber *floatInstance = [NSNumber numberWithFloat: 100.0F]; } ``` 2. **物件方法 `initWith<類型名稱>`**:當你手動初始化物件的記憶體後,可以透過以下格式初始化 ```objectivec= NSNumber *物件名 = [[NSNumber alloc] initWith<類型名稱>]; ``` 範例: ```objectivec= #import <Foundation/Foundation.h> void number_2(void) { NSNumber *boolInstance = [[NSNumber alloc] initWithBool: NO]; NSNumber *shortInstance = [[NSNumber alloc] initWithShort: 1]; NSNumber *longInstance = [[NSNumber alloc] initWithLong: 2L]; NSNumber *floatInstance = [[NSNumber alloc] initWithFloat: 300.0F]; } ``` ### NSNumber 取數值 * `NSNumber` 物件取數值的格式如下 ```objectivec= [物件名 <數據類型>Value] ``` 範例: ```objectivec= void number_3(void) { NSNumber *boolInstance = [NSNumber numberWithBool: YES]; NSLog(@"bool value: %i", [boolInstance boolValue]); NSNumber *shortInstance = [NSNumber numberWithShort: 1]; NSLog(@"short value: %i", [shortInstance shortValue]); NSNumber *longInstance = [NSNumber numberWithLong: 100L]; NSLog(@"long value: %lli", [longInstance longLongValue]); NSNumber *floatInstance = [NSNumber numberWithFloat: 100.0F]; NSLog(@"float value: %f", [floatInstance floatValue]); } ``` > ![](https://hackmd.io/_uploads/SyK5adkih.png) ## 字串物件 字串物件主要分為可修改、不可修改兩種類型 :::info * **字元、字串** 差別 1. 使用的格式不同,字串必須以 `@""` 包裹、字元則使用 `''` 包裹 ```objectivec= void charStr(void) { char c = 'a'; NSString *str = @"a"; } ``` 2. 字串的結尾自動會帶上 `\0` (與 C 相同);所以一個字串消耗的記憶體空間為字串長度 + 1 ::: ### NSString 不可修改字串 - 創建 * `NSString` 創建出的字串是不可修改的,並且 **它並不放置在記憶體堆區**(所以不能用引用記數計算);基本使用格式如下 ```objectivec= NSString *物件名 = @"字串"; // 字串輸出格式 %@ NSLog(@"%@", 物件名); ``` * **NSString 類方法** 1. **`stringWithString` 類方法**:用另一個字串來初始化 ```objectivec= void createWithString(void) { NSString *str = @"HelloWorld"; NSString *newStr = [NSString stringWithString:str]; NSLog(@"new string: %@", newStr); } ``` > ![](https://hackmd.io/_uploads/HJWZZFki2.png) 2. **`stringWithFormat` 類方法**:使用類似 **NSLog()** 格式建立字串,也就是使用 `%@` 來標記字串位置 ```objectivec= void createWithFormat(void) { NSString *str = @"HelloWorld ~ "; NSString *newStr = [NSString stringWithFormat:@"I'm format. %@", str]; NSLog(@"new string: %@", newStr); } ``` > ![](https://hackmd.io/_uploads/HyepZFyo3.png) * **NSString 物件方法** 1. **`stringByAppendingString` 物件方法**:對於已將創建出的 NSString 仍可以使用 `stringByAppendingString` 來創建新字串 ```objectivec= void createWithAppend(void) { NSString *str = @"HelloWorld ~ "; NSString *newStr = [str stringByAppendingString:@"Append string."]; NSLog(@"new string: %@", newStr); } ``` > ![](https://hackmd.io/_uploads/Sk93zYks2.png) ### NSString 比較內容 * `isEqualToString` 比較字串內容(可直接比較字串內容) ```objectivec= void stringEquals(void) { NSString *str = @"HelloWorld ~ "; bool isSame = [str isEqualToString:@"HelloWorld 123"]; NSLog(@"Is `HelloWorld 123` same with target: %i", isSame); isSame = [str isEqualToString:@"HelloWorld ~ "]; NSLog(@"Is `HelloWorld ~ ` same with target: %i", isSame); } ``` > ![](https://hackmd.io/_uploads/BytdNFyin.png) * 字串的前(`hasPrefix`)後(`hasSuffix`)綴比較 ```objectivec= void stringEquals_2(void) { NSString *str = @"HelloWorld ~ "; // 前綴比較 bool hasRes = [str hasPrefix:@"HelloB"]; NSLog(@"Is target has prefix `HelloB`: %i", hasRes); // 後綴比較 hasRes = [str hasSuffix:@"~ "]; NSLog(@"Is target has suffix `~ `: %i", hasRes); } ``` > ![](https://hackmd.io/_uploads/SJfBHF1sn.png) ### NSString 大小寫轉換、切割字串 * NSString 大小寫轉換 ```objectivec= void modifyStringCase(void) { NSString *str = @"HelloWorld ~ "; NSLog(@"upper target string: %@", [str uppercaseString]); NSLog(@"lower target string: %@", [str lowercaseString]); } ``` > ![](https://hackmd.io/_uploads/rJcELYys2.png) * NSString 切割字串有幾種方式 1. 切割至(包含結尾):`substringToIndex` 2. 從某個點開始切割(包含開頭):`substringFromIndex` 3. 切割某個範圍:`substringWithRange` :::info * 在這裡的 index 是從 1 開頭計算 * `substringWithRange` 接收的是 `NSRange`,而 `NSRange` 是一個 struct 結構,它不能作為物件創建(不能 `new`) ::: ```objectivec= void cutStringByIndex(void) { NSString *str = @"HelloWorld ~ "; NSLog(@"cut to: %@", [str substringToIndex:1]); NSLog(@"cut from: %@", [str substringFromIndex:1]); NSRange range = { 3, 7 }; NSLog(@"cut from: %@", [str substringWithRange:range]); } ``` ### NSMutableString 可修改字串 - 使用 * `NSMutableString` 是 `NSString` 的子類,它是一個可修改內容的字串,**放在記憶體堆區**(可用引用計數計算);基本使用格式如下 ```objectivec= NSMutableString *mStr = [NSMutableString stringWithString: @"字串"]; ``` * **`NSMutableString` 常見使用方式** 1. `appendString` 附加字串 ```objectivec= void mutableString(void) { NSString *baseStr = @"Hey~ "; NSMutableString *mStr = [NSMutableString stringWithString: baseStr]; [mStr appendString:@"Kyle"]; NSLog(@"current string: %@\n", mStr); } ``` > ![](https://hackmd.io/_uploads/ry-sJ9yoh.png) 2. `appendFormat` 附加字串(使用 NSLog 的字串方式添加) ```objectivec= void mutableString_2(void) { NSString *personName = @"Alien"; NSMutableString *mStr = [NSMutableString stringWithString: @"Yo yo ~ "]; [mStr appendFormat:@"What up %@", personName]; NSLog(@"current string: %@\n", mStr); } ``` > ![](https://hackmd.io/_uploads/SJG4ecksn.png) ## 陣列 ### 傳統陣列 `[]` * 傳統陣列的用法與 C 語言相同,不加以贅述,請看以下範例 ```objectivec= // 陣列宣告格式 類型 變數名稱[數量]; ``` 1. 一維陣列 ```objectivec= void arrayTest(void) { // 將陣列內容初始化為 0 int helloArray[10] = {0}; helloArray[0] = 99; // 遍歷 Array for(int i = 0; i < 10; i++) { NSLog(@"array index: %i, value: %i\n", i, helloArray[i]); } } ``` > ![](https://hackmd.io/_uploads/SJ35Coksh.png) 2. 二維陣列 ```objectivec= void arrayTest_2(void) { int helloArray[10][5] = {0}; helloArray[3][2] = 100; for(int i = 0; i < 10; i++) { for(int j = 0; j < 5; j++) { NSLog(@"array index: [%i][%i], value: %i\n", i, j, helloArray[i][j]); } } } ``` > ![](https://hackmd.io/_uploads/HyHfAiJi3.png) ### NSArray 不可修改陣列 * `NSArray` 是 **陣列類別**,**必須以 `nil` 作為陣列結尾** > ![](https://hackmd.io/_uploads/SyM7bn1i2.png) :::success * `nil` & `Nil` 差別:兩者都是特殊字符,但描述的目標不同 * `nil` 用於描述 **空物件** * `Nil` 則用於描述 **空類** ::: 範例如下: ```objectivec= void nsArrayTest(void) { NSArray *myArray = [NSArray arrayWithObjects:@"Apple", @"Banana", @"Car", nil]; for(int i = 0; i < [myArray count]; i++) { // 由 index 取得物件 NSLog(@"array index: %i, value: %@\n", i, [myArray objectAtIndex:i]); } // 由物件取得 index NSLog(@"get index from object, index: %lu\n", (unsigned long)[myArray indexOfObject:@"Car"]); } ``` > ![](https://hackmd.io/_uploads/H10RMhJo3.png) ### NSMutableArray 可修改的陣列 * `NSMutableArray` 作為可修改的陣列類,就代表我們可以對該類的物件進行增刪改查... 的行為;初始化方式如下 ```objectivec= NSMutableArray *物件名稱 = [NSMutableArray new]; ``` 使用範例 ```objectivec= void nsMutableArrayTest(void) { NSMutableArray *mArray = [NSMutableArray new]; // 添加物件 [mArray addObject:@"Hello"]; [mArray addObject:@"World"]; [mArray addObject:@"123"]; // 遍歷 Array for(int i = 0; i < [mArray count]; i++) { NSLog(@"array index: %i, value: %@\n", i, [mArray objectAtIndex:i]); } // 移除物件 [mArray removeObject:@"Hello"]; // 插入一個元素在當前最後一個元素之前 [mArray insertObject:@"Yoyo" atIndex:[mArray indexOfObject:[mArray lastObject]]]; // 遍歷 Array for(int i = 0; i < [mArray count]; i++) { NSLog(@"array index: %i, value: %@\n", i, [mArray objectAtIndex:i]); } } ``` > ![](https://hackmd.io/_uploads/SJD-Hh1sn.png) ## 字典 字典就如同 Java 中的 Map 集合,同用採用 **鍵值對**(也就是 Key & Value),並且不會按照順序進行排序 **並且字典的最後一個元素同樣要以 `nil` 作為結尾** ### NSDictionary 不可修改的字典 * `NSDictionary` 是不可修改的集合,範例如下 比較特別的地方在 **`dictionaryWithObjectsAndKeys` 初始化時,是先寫 Value 再寫 Key** ```objectivec= void nsDicTest(void) { NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: // value, Key [NSNumber numberWithInt:7], @"Apple", // value, Key [NSNumber numberWithInt:99], @"Banana", // 結尾必須 nil nil]; NSUInteger count = [dictionary count]; NSLog(@"Count: %lu", (unsigned long) count); // 透過 key 取得 value NSNumber *n= [dictionary objectForKey:@"Apple"]; NSLog(@"Use key find value: %d", [n intValue]); // 遍歷 Dictionary for(NSString *key in dictionary.allKeys) { NSNumber *value = [dictionary objectForKey:key]; NSLog(@"Key: %@, Value: %i\n", key, [value intValue]); } } ``` > ![](https://hackmd.io/_uploads/H1tz2nJsh.png) ### NSMutableDictionary 可修改的字典 * `NSMutableDictionary` 是可修改的集合,可在運行時動態添加元素進入,範例如下 ```objectivec= void nsMutableDicTest(void) { NSMutableDictionary *mDic = [NSMutableDictionary new]; [mDic setObject:@"Alien" forKey:@"Key1"]; [mDic setObject:@"Kyle" forKey:@"Key2"]; [mDic setObject:@"Kyle" forKey:@"Key3"]; NSUInteger count = [mDic count]; NSLog(@"Count: %lu", (unsigned long) count); for(NSString *key in mDic.allKeys) { NSString *value = [mDic objectForKey:key]; NSLog(@"Key: %@, Value: %@\n", key, value); } } ``` 從結果可以看出,Dictionary(可修改、不可修改)並非按照順序儲存元素 > ![](https://hackmd.io/_uploads/BJbF_cxih.png) ## Set Set 與 Array 相似,它用來儲存一系列的數值,但不同點在於 1. **它不會儲存重複元素**,如果有重複元素它就會忽略 2. 不按照順序儲存 ### NSSet 不可修改的 * `NSSet` 是不可修改的集合 ```objectivec= void nsSet(void) { NSSet *set = [NSSet setWithObjects:@"Apple", @"Banana", @"Orange", @"Apple", nil]; NSUInteger count = [set count]; NSLog(@"Count: %lu", (unsigned long) count); for(NSString *item in set) { NSLog(@"Set value: %@\n", item); } NSSet *set2 = [NSSet setWithObjects:@"Apple", @"Banana", @"Orange", nil]; NSLog(@"is same element: %i", [set isEqualToSet:set2]); } ``` 從結果可以發現:儲存元素確實不按照順序,並且有相同元素也會被過濾掉 > ![](https://hackmd.io/_uploads/SJZ65qgoh.png) ### NSMutableSet 可修改的 * `NSMutableSet` 是可修改的集合,可以在運行時動態加入、移除元素,但仍符合元素不可重複的規則 ```objectivec= void nsMutableSet(void) { NSMutableSet *mSet = [NSMutableSet new]; [mSet addObject:[NSNumber numberWithInt:123]]; [mSet addObject:[NSNumber numberWithLong:100L]]; [mSet addObject:[NSNumber numberWithFloat:0.1413F]]; NSUInteger count = [mSet count]; NSLog(@"Count: %lu", (unsigned long) count); for(NSNumber *item in mSet) { NSLog(@"Set value: %@\n", item); } [mSet removeObject:[NSNumber numberWithInt:123]]; NSLog(@"\nAfter remove element"); for(NSNumber *item in mSet) { NSLog(@"Set value: %@\n", item); } } ``` > ![](https://hackmd.io/_uploads/HJWvh5eo2.png) ## Appendix & FAQ :::info ::: ###### tags: `iOS`