---
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]);
}
```
> 
## 字串物件
字串物件主要分為可修改、不可修改兩種類型
:::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);
}
```
> 
2. **`stringWithFormat` 類方法**:使用類似 **NSLog()** 格式建立字串,也就是使用 `%@` 來標記字串位置
```objectivec=
void createWithFormat(void) {
NSString *str = @"HelloWorld ~ ";
NSString *newStr = [NSString stringWithFormat:@"I'm format. %@", str];
NSLog(@"new string: %@", newStr);
}
```
> 
* **NSString 物件方法**
1. **`stringByAppendingString` 物件方法**:對於已將創建出的 NSString 仍可以使用 `stringByAppendingString` 來創建新字串
```objectivec=
void createWithAppend(void) {
NSString *str = @"HelloWorld ~ ";
NSString *newStr = [str stringByAppendingString:@"Append string."];
NSLog(@"new string: %@", newStr);
}
```
> 
### 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);
}
```
> 
* 字串的前(`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);
}
```
> 
### NSString 大小寫轉換、切割字串
* NSString 大小寫轉換
```objectivec=
void modifyStringCase(void) {
NSString *str = @"HelloWorld ~ ";
NSLog(@"upper target string: %@", [str uppercaseString]);
NSLog(@"lower target string: %@", [str lowercaseString]);
}
```
> 
* 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);
}
```
> 
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);
}
```
> 
## 陣列
### 傳統陣列 `[]`
* 傳統陣列的用法與 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]);
}
}
```
> 
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]);
}
}
}
```
> 
### NSArray 不可修改陣列
* `NSArray` 是 **陣列類別**,**必須以 `nil` 作為陣列結尾**
> 
:::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"]);
}
```
> 
### 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]);
}
}
```
> 
## 字典
字典就如同 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]);
}
}
```
> 
### 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(可修改、不可修改)並非按照順序儲存元素
> 
## 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]);
}
```
從結果可以發現:儲存元素確實不按照順序,並且有相同元素也會被過濾掉
> 
### 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);
}
}
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `iOS`