今さらまとめるObjective-C文法基礎

案件でObjective-Cに携わることになり、今さらながらObjective-Cの構文を学習したので備忘録としてまとめました。
※ SwiftでiOSアプリを作ったことがある人向けです。

# 変数・定数・型

## 宣言構文

```objective_c
// (1) 型を宣言をして代入する　
int age
age = 23;

// (2) 型宣言と同時に代入も可能
int age = 23;

// (3) 同じデータ型の場合は複数同時に宣言可能
int width = 100, height = 88, area;
```

## 型

### 基本データ型

| データ型 | 説明 |
|:-----------------|:------------------|
| short int | 16ビット符号付き整数 |
| int | 32ビット符号付き整数 |
| long int | 64ビット符号付き整数 |
| float | 32ビット符号付き少数。代入時数値の末尾にfが必要|
| double | 64ビット符号付き少数 |
| BOOL | 真偽値。**YES または NO で表現する** |
| char | 半角英数字1文字のみ代入可能。 **代入する文字はシングルクォーテーションで囲む** |

※ `short int`, `int`, `long int`型のビット数はビルド環境によって変化するらしい。
※ Bool型は `true`/`false` でも真偽値になる。また、`0` が `NO` で `それ以外の数値` は `YES` になる。


### ポインタ型（参照型）

基本データ型以外は全てポインタ型になる。

```objective_c
// ポインタ型変数として宣言する変数名の前に * （アスタリスク）をつける
NSString *name = @"太郎";
```

### id型
どんな型でも代入可能な汎用的な型（Javaで言う `Object型` ）。
参照型の1つだが、宣言時に `*` が不要。


## 定数

### const

変数宣言時に`const`をつけることで定数を定義可能。
通常、ヘッダファイルまたはメインファイルの `@interfece` 文で宣言する。

```objective_c
// const はデータ型の前後どちらにつけても良い
const int LEVEL = 27;
NSString * const COLOR = @"green";
```

### #define（マクロ定義）

`#define`で指定された文字はコンパイル前に指定した値で書き換えられる。
`#undef`を使うと、それ以降は文字の書き換えが行われなくなる。

```objective_c
// ; は不要
#define LIMIT 100
#define URL @"hoge.com"
// マクロ関数も定義可能
# define test(a, b) a + b
NSLog(@"%d", test(1, 2));    // => 3
```

### enum

連続する整数を定義可能。

```objective_c
enum {
  NUM_A = 10,
  NUM_B,    // => 11
  NUM_C,    // => 12
  NUM_D,    // => 13
  NUM_E = 99, 
  NUM_F,    // => 100
}
```

typedefを使ってenumをデータ型として定義する。

```objective_c
typedef enum {
  SUNNY,    // => 0
  CLOUDY,   // => 1
  RAINY,    // => 2
} Weather;
```

**`NS_ENUM` を使う（この書き方が主流）**

```objective_c
typedef NS_ENUM(NSInteger, Weather) {
  WeatherSUNNY,    // => 0
  WeatherCLOUDY,   // => 1
  WeatherRAINY,    // => 2
};
```

`NS_ENUM` の利点

- switch文で記述漏れがないかコンパイラがチェックしてくれる
- 型を明示する必要があるため、より型安全



## 文字列 - NSString

```objective_c
// 宣言 + 代入
NSString *name = @"太郎";

// 比較
if ([@"hoge" isEqualToString:@"huga"]) {    //=> false
}

// 連結
NSString *userName = [name stringByAppendingString:@" 様"];

// 文字数カウント
NSInteger length = [name length];
// これでもOK　プロパティはないがコンパイラが引数なしのメソッドと判断する
NSInteger length = name.length

// 数値 → 文字列
NSString *age = [NSString stringWithFormat:@"%d", num];

// 文字列 → 数値
NSInteger *num = [@"1000" integerValue];
```

※ 文字列に変更を加えたい場合は `NSMutableString` クラスを使う

## 配列 - NSArray, NSMutableArray

- `NSArray` : 要素の個数が固定
- `NSMutableArray` : 要素の個数が可変

```objective_c
// 宣言 + 代入
NSArray *alphabet = @[@"A", @"B", @"C"];
// 配列の要素はオブジェクトのみ　数値型やBOOL型を入れたい場合はNSNumberクラスとして入れる
int num = 10;
NSArray *numbers = @[@23, @190, @4, @(num)];

// 参照
NSString *b = alphabet[1];
// 末尾要素の参照（配列が空の場合はnil）
NSString *lastAlphabet = [alphabet lastObject];

// 要素数の取得（count はプロパティではなくメソッドだが、この書き方が可能）
int count = alphabet.count

// -- 以下、NSMutableArrayのみ

// 要素の追加
// 空の配列はNSMutableArrayクラスの親クラスのNSArrayクラスの array メソッドで作成可能
NSMutableArray *array [NSMutableArray array];
[array addObject:@"hoge"];

// 要素の挿入
NSMutableArray *greets [NSMutableArray arrayWithArray:@[@"こんにちは", @"Hello", @"Moi"]];
// atIndex番目に挿入する（先頭： 0）
[greets insertObject:@"你好" atIndex:1];    // => (こんにちは, 你好, Hello, Moi)

// 要素の削除
[greets removeObjectAtIndex:1];       // => (こんにちは, Hello, Moi)
[greets removeObject:@"こんにちは"];    // => (Hello, Moi)
```

## 辞書 - NSDictionary, NSMutableDictionary

```objective_c
// 宣言 @{キー:値, ...} の形式で書く　キーはNSString, 値はオブジェクトのみ
NSDictionary *wether = @{@"sunny":@"晴れ", @"cloudy":@"曇り", @"rainy":@"雨"};
// 参照
NSLog(@"%@", wether[@"sunny"]);    // => 晴れ

// -- 以下、NSMutableDictionaryのみ

// 要素の追加
NSMutableDictionary *hoge = [NSMutableDictionary dictionaryWithCapacity:3];
hoge[@"A"] = @1;
hoge[@"B"] = @2;

// 値の変更（キーがない場合は追加になる）
hoge[@"B"] = @2222;

// 要素の削除（キーがない場合は何も起きない）
[hoge removeObjectForKey:@"A"];
[hoge removeAllObjects];
```

## 構造体 - Struct

```objective_c
// メンバにARCで管理されているオブジェクトを持つことはできない
// 定義（typedefを使って型として定義されることが多い）
struct _screen {
  unsigned int width;
  unsigned int height;
} Screen

// 宣言 + 代入
Screen iPhone;
iPhone.width = 5;
iPhone.height = 24;
```

# 制御構文

## 条件分岐 - if

条件式の結果が`0`, `NO`, `NULL`, `nil`以外の時、if文の中を実行する。

```objective_c
// 普通にif文が書ける ※else ifもモチロン書ける
int age = 35;
if (age >= 20) {
  NSLog(@"You can drink.");
} else {
  NSLog(@"You cannot drink.");
}
// => You can drink.
```

## 条件分岐 - switch

case文の値は`数値`, `char`, `定数`のみ設定可能。

```objective_c
int rank = 'B';
switch(score) {
  case 'A':
    NSLog(@"Your rank is A.");
    break;
  case 'B':
    NSLog(@"Your rank is B.");
    break;
  case 'C':
    NSLog(@"Your rank is C.");
    break;
  default:
    NSLog(@"You are not ranker...");
    break;
}
// => Your rank is B.
```

## 繰り返し - for

```objective_c
// カウンタを使う
for (int i = 0; i < 10; i++) {
  NSLog(@"%d", i);
}

// コレクションクラスのfor inを使う
NSArray *array = [@1, @2, @3];
NSInteger sum = 0;
for (NSNumber *num in array) {
  sum += [num intValue];
}
NSLog(@"%d", sum);

// NSArrayでenumerateObjectsUsingBlockを使う（推奨）
NSArray *array = [@1, @2, @3];
__block NSInteger sum = 0;
[array enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
  sum += obj;
}];
NSLog(@"%d", sum);
```
NSArrayの `enumerateObjectsUsingBlock` メソッドについては、こちらの記事の解説が分かりやすいです。
[NSArrayでfor(; ;)とかfor-inを使うのをやめて、enumerateObjectsUsingBlock:を使う](https://qiita.com/exilias/items/f8ebd0dfed493bb0e25a)


## 繰り返し - while

条件式が`YES`の間繰り返す。

```objective_c
while (YES) {
  // 無限ループ
}
```


# ログ出力 - NSLog

```objective_c
// NSLog(文字列オブジェクト);
NSString *name = @"太郎";
NSLog(str);    // => 太郎
// NSLog(フォーマット指定子を含んだ文字列オブジェクト, 値1, 値2, ...);
NSLog(@"こんにちは、%s", name);    // => こんにちは、太郎

// BOOL型の値は %d でフォーマットし、YES=1, NO=0を出力する
BOOL isOK = YES;
NSLog(@"Are you OK? %d", isOS);    // => Are you OK? 1
```


# クラス構文

クラス定義はヘッダファイル `hoge.h` とメインファイル（実装ファイル） `hoge.m` に分かれて定義される。

## ヘッダファイルの構造
```objective_c:hoge.h
#import <フレームワーク/ファイル>
#import ソースファイル

// @interface クラス名 : 継承元クラス名 <プロトコル>
interface Hoge : Huga <MyProtcol> {
  // インスタンス変数宣言
  // データ型　変数名;
  NSInteger num1, num2;
  NSArray *array;  
}
// プロパティ変数宣言
// @property (属性) データ型 プロパティ名;
@property (assign, readonly, noatomic) NSInteger * num;
// assign: 参照ではなく値を割り当てる
// strong: ARCでカウントする参照（初期値）
// weak  : ARCでカウントしない参照
// copy  : オブジェクトをコピーする
// --
// readonly : 読み出しのみ
// readwrite: 読み書き可能（初期値）
// --
// noatomic　　　: アトミック（別スレッドから同時に参照された際に同値を保証する状態）ではない（初期状態はアトミック）
// getter=ゲッタ名: 独自のゲッタメソッドを指定（noatomicを指定した場合のみ）
// setter=セッタ名: 独自のセッタメソッドを指定（noatomicを指定した場合のみ） 

// メソッド宣言
// + (戻り値の型) メソッド名:(引数1の型)引数1 ラベル2:(引数2の型)引数2 ...;
// - (戻り値の型) メソッド名:(引数1の型)引数1 ラベル2:(引数2の型)引数2 ...;
// + はクラスメソッド
// - はインスタンスメソッド
- (NSString *) fullName:(NSString *)firstName lastName:(NSString *)lastName;
@end
```

## メインファイルの構造
```objective_c:hoge.m
#import ヘッダファイル

// @interface クラス名() <プロトコル>
// @interface ~ @end　はクラスエクステンションと呼ぶ
// クラス内のみで使えるインスタンス変数、プリパティ変数、メソッドを宣言できる（子クラスからも使えない）
@interface Hoge() <MyProtcol> {
  // インスタンス変数宣言
}
// プロパティ変数宣言
// メソッド宣言
@end 

// @implementation クラス名
@implementation Hoge
// @implementation ~ @end　はインプリメンテーションセクションと呼ぶ
// ヘッダファイル, クラスエクステンションで宣言したプロパティの初期化やメソッドの定義を行う
- (NSString *) fullName:(NSString *)firstName lastName:(NSString *)lastName {
  return [NSString stringWithFormat:@"%@ %@", firstName, lastName];
}
- (void) countup {
  // 自身のプロパティにアクセスする際は _ をつける　か self をつける
  _count++;    // self.count++; でもOK
}
@end


```

## クラスの使い方

```objective_c
// インスタンスの作成/初期化 alloc でメモリ確保 init で初期化
NSDate *theDate = [[NSDate alloc] init];

// コンビニエンスコンストラクタ
// 例えば、NSDateクラスでは、allocし現在時刻でinitしたNSDate型を返すdateクラスメソッドが用意されている
NSDate *today = [NSDate date];

// プロパティへのアクセス
int huga = myObj.hoge;

// メソッドの実行（メッセージ式） [クラス名/インスタンス名 メソッド名:引数];
[hogeObj hogeFunc:100];
// 複数引数の場合はラベルをつける 引数1のラベルがメソッド名なことに注意
[hogeObj hogeFunc:10 a:82 b:@"b"];
```


# 参考文献
- 大重美幸, 詳細！Objective-C iPhoneアプリ開発入門ノート Xcode5+iOS7対応, 株式会社 ソーテック社, 2013.