Objective-Cを始めたのでそのメモとか。


最近、分けあってObjective-Cを学び始めました。
ここは最初に躓いたり、あるいは今後どっかで使えそうだなーと思ったものをただひたすらメモしていくところです。

※Xcode関連は別にまとめたほうがいい気がしたので[別記事](http://qiita.com/edo_m18/items/1ad3f81a3081f34d6151)にしました。

--------

##ライフサイクル

iOSアプリは以下のライフサイクルでデリゲートメソッドが呼ばれる。
本来はもう少し細かい分岐処理とかあるけど、大まかには以下の順番。
（なにがどのタイミングで存在するか、みたいなのは未調査）

| UIApplicationDelegate | 最初のViewController |
|-----------------------|---------------------|
| application:willFinishLaunchingWithOptions: | -- |
| application:shouldRestoreApplicationState:| -- |
| application:didDecodeRestorableStateWithCorder:| -- |
| application:didFinishLaunchingWithOptions: | loadView |
| -- | viewDidLoad |
| -- | viewWillAppear: |
| applicationWillEnterForeground: | viewWillLayoutSubviews |
| applicationDidBecomeActive: | viewDidLayoutSubviews |
| -- | viewDidAppear: |

ちなみに、バックグラウンドから復帰した場合はViewController側は呼ばれないので注意。

Storyboardを使った場合の起動シーケンスは以下。

1. アプリケーションの起動
2. Storyboardファイルが読み込まれる
3. ファイル内オブジェクトのインスタンス化と初期化
4. Storyboardで設定されている属性を各インスタンスに反映
5. 各インスタンスのawakeFromNibが呼ばれる
6. アプリケーション起動完了

--------

##色々なチェック

###マルチタスク対応か確認

```objc
UIDevice *device = [UIDevice currentDevice];BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
{   backgroundSupported = device.multitaskingSupported;
}
```

###subviewをチェック
インスタンス変数に操作したいViewを保持しておかなくても、subviewをたどって処理をしてみる。
ほんとはもっといい方法があるのかもしれないけど、とりあえず。

```objc
for (UIView *view in self.view.subviews)
{
	if ([view isKindOfClass:[Hoge class]])
	{
		//該当クラスのviewなら処理
	}
}
```

subviewはまさに`subviews`というプロパティでアクセスできる。
それをfor inで回して、さらに取り出したviewが目的のクラスかどうかをチェックしている。

-----------------------------------------------

##@ディレクティブ

色々記事を見ていると古いものから新しいものまであるけど、最近のiOSでは「@ディレクティブ」でのショートカットが増えてきた印象？

* @"text" … NSString型のリテラル
* @5 … NSNumber型のリテラル
	* @5 … int
	* @5U … unsigned int
	* @5L … long
	* @5LL … long lon
	* @5.0F … float
	* @5.0 … double
	* @YES … BOOL
* @[…] … NSArray型のリテラル
* @{@"key": @"value"} … NSDictionary型のリテラル

-----------------------------------------------

##便利ツールとかもろもろメモ

開発に必要なツールとかも大事なので、そのへんもメモしていく。

* [Cocoapods](http://cocoapods.org/)
iOSやMacアプリを作る際に便利なライブラリ管理ツール。gemやnpm、Home brewみたいな感じかな？ [紹介記事はこちら](http://www.iosjp.com/dev/archives/451)  
実際に使うには、npmのpackage.jsonのように設定ファイル（Podfile）を生成して、その中に色々インストールしたいライブラリ名やバージョンを書くみたい。

```Podfile-sample
platform:ios
pod 'CocoaLumberjack'
```

* Xxx-Prefix.pch
全ファイル共通で読み込まれるファイル。全体で共通の設定などをここに書いておくと個別にヘッダファイルを指定しなくてもいいので便利。

-----------------------------------------------

##動的に色を設定する

色を設定するには、`UIColor`クラスを用いるよう。

```objc
hoge.backgroundColor = [UIColor redColor]; //赤を設定

//RGBAからカラーを生成
UIColor *color = [UIColor colorWithRed:0.0 green:0.5 blue:0.8 alpha:1.0];
```

-----------------------------------------------

##よく使いそうなUILabelの設定メモ

```objc
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 50)];
label.text         = @"テキスト";
label.font         = [UIFont systemFontOfSize:16];
label.textColor    = [UIColor blackColor];
label.shadowColor  = [UIColor whiteColor];
label.shadowOffset = CGSizeMake(1, 1);
```

###ぼけた影のあるUILabelを作る

ちなみにこの`shadow***`はぼかしのついた影は作れないらしいので、ぼけた影を付けたい場合はUILabelを継承したカスタムクラスを作らないとならないぽい。

```objc
@interface ShadowLabel : UILabel
@end

@implementation ShadowLabel

- (void)drawRect:(CGRect)rect
{
	CGContextRef context = UIGraphicsGetCurrentContext();
	CGContextSaveGState(context);
	CGContextSetShadow(context, CGSizeMake(2, -2), 5);
	[super drawRect:rect];
	CGContextRestoreGState(context);
}

@end
```

`drawRect:`メソッドは描画を担当するメソッド。それをオーバーライドすることで対応。
（内部実装追っていないけど、これを見るにCGContextでレンダリングを行っているぽい？）
親クラスの`drawRect:`メソッドを実行する前にCGContextの状態をごにょごにょすることで割り込みする、というイメージかな。

これを応用すれば色々できそう。

-----------------------------------------------

##ランタイムな調査

###動的にクラスの調査をする

Objective-Cはどうやらだいぶ柔軟な言語のよう。
JSでやっていたような、引数チェックみたいなことができる。

クラスに関する情報は`class`メソッドから取得することができる。

```objc
- (void)method:(id)object
{
	//引数object（インスタンス）からクラスオブジェクトを取得する
	Class cls;
	cls = [object class];

	//クラス自体からクラスオブジェクトを取得する
	Class stringCls;
	stringCls = [NSString class]; //クラス・メソッドとしてもclassがある

	//文字列からクラスオブジェクトを取得する
	Class myClass;
	myClass = NSClassFromString(@"MySuperClass");
}
```

###プロトコルに準拠しているか調査

```objc
- (void)method:(id)object
{
	Class cls;
	cls = [object class];
	
	if ([cls conformsToProtocol:@protocol(HogeProtocol)]) {
		//do something.
	}
}
```


###クラス・継承のチェック

JSでいうところの`instanceof`や`isPrototypeOf`的な感じだと思う。

```objc
- (void) method:(id)object
{
	//objectがNSStringクラスを継承しているかチェック
	if ([object isKindOfClass:[NSString class]]) {
		//NSStringを継承
	}

	//objectがまさに特定のクラスかをチェック
	if ([object isMemberOfClass:[NSString class]]) {
		//NSStringクラスそのもの
	}
}
```

###AppDelegateの参照を得る

```objc
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate doSomething];
```

-----------------------------------------------

##NSLog関連

###CGRect, CGSize, CGPointをNSLogする

これら3つは構造体のため、id型の出力である`NSLog(@"%@", hoge)`は使えない。
そのため、`NSStringFromCG****()`関数を利用して出力する。（***はそれぞれRectなどが入る）

例)

```objc
NSLog(@"%@", NSStringFromCGRect(hoge);
```

-----------------------------------------------

##アプリのアイコン設定でハマる
ハマったと言えるほど時間はかからなかったけれど、いちおうメモｗ
アプリのアイコンをPhotoshopで作って設定したら、なんだかこんなエラーが。

`"None of the input catalogs contained a matching app icon set named "AppIcon"".`

検索してみると、[StackOverflowに解決策](http://stackoverflow.com/questions/20483713/asset-catalog-compiler-error)がありました。
どうやらPhotoshopから書きだしたときにpngがおかしくなってる模様。

ImageOptimとかのツールを使って変換することで、エラーなく使用することが出来た。

-----------------------------------------------

##ブロック構文

JavaScriptではおなじみの関数を渡す書式。
C#とかではラムダ式。それをObjective-Cで実現する仕組み。

```objc
^(仮引数) {
	//実行する処理
};

戻り値 (^変数名)(引数) = ^(仮引数) {
	//実行する処理
};
```

実際のコードだと以下。

```objc
^(int x) {
	return x * x;
};

int (^hoge)(int) = ^(int x) {
	return x * x;
};

NSLog(@"int is %d", hoge(5));
```

###__block修飾子

**追記:`__block`修飾子についてコメントをもらいました。変数を上書きする必要がある場合のみ、宣言が必要です。（読み込みはできる）**

通常、ブロック構文内では外部で宣言された変数に書き込みできない。
そのため、書き込みしたい変数に`__block`修飾子をつけると書き込むことができる。

```objc
__block int i = 0;

void (^printInt)(void) = ^(void) {
	//書き込む必要がある場合のみ、`__block` 修飾子が必要。
	//i = 5;
	NSLog(@"%d", i);
};

printInt() //=> 0;
```

###ブロックの型を定義する

typedefを使用することで、ブロック構文も定義を行うことができる。

```objc
typedef BOOL (^someCheck)(int) = ^(int x);

someCheck check1 = ^(int x) {
	if (x < 0) {
		return YES;
	}
	else {
		return NO;
	}
};
```

###ブロック構文を利用したTips

StackOverflowで見かけた手法。
NSDictionaryを使ったもの。

```objc
typedef void (^Callback)();

NSString *callbackType = @"callback1";
NSDictionary *d = @{
	@"callback1":
	^{
		NSLog(@"invoked callback1");
	},
	@"callback2":
	^{
		NSLog(@"invoked callback2");
	}
};

((Callback)d[callbackType])();
```

JSで書くと多分こんな感じのこと。

```sample.js
var callbackType = 'callback1';
var d = {
	"callback1": function () {
		alert('invoked callback1");
	},
	"callback2": function () {
		alert('invoked callback2");
	}
};

d[callbackType]();
```

-----------------------------------------------

##delegateやイベントなど細々まとめ

###UIBarButtonItemのアクション

UIBarButtonItemは、UIButtonの`addTarget:action:forControlEvents:`でのdelegate設定がなく、以下のように`setAction`と`setTarget`を利用する必要があった。

```objc
UIBarButtonItem *btn = [[UIBarButtonItem alloc] init];
[btn setTarget:self];
[btn setAction:@selector(action)];
```

-----------------------------------------------

##値の取得

###ステータスバーのframe
以下の方法で取得できる。
ただし、回転方向を考慮していないので横向きの場合は`height`が幅の数値を返すので、回転しているかどうかを考慮してプログラムしないとおかしなことになるので注意。

```objc
CGRect frame = [UIApplication sharedApplication].statusBarFrame;
```

-----------------------------------------------

##参考にした記事まとめ

* [iOSのカメラ機能を使う方法](http://dev.classmethod.jp/smartphone/ios-camera-intro/)
* [iOS アプリの構造がどのようになっているか紐解いてみる](http://d.hatena.ne.jp/glass-_-onion/20120507/1336320038)
* [iOS アプリの画面開発の基礎を理解する](http://d.hatena.ne.jp/glass-_-onion/20120601/1338477967)
* [iOS のイベント駆動をライフサイクルイベントとユーザアクションイベントにわけて理解する](http://d.hatena.ne.jp/glass-_-onion/20120405/1333611664)