yujiro's blog

「インターネット上で正しい答えを得る最善の方法は、質問することではない。間違った答えを投稿することだ」by ウォード・カニンガム

Objective-C について開発に最低限必要な知識

3月からiOS 開発してます。

現場では、iOSソースコードは全面Swift化の方向で進んでいるのですが、まだObjective-Cソースコードが多く残っている状態でして、まぁそういう現場は多いのではないかと思います。

僕としても本腰いれて勉強するつもりはないけど、卒なく一通りのことはできるようにしておきたいみたいな温度感です。

ここではObjective-C を扱う上で最低限の知識をまとめたいと思います。

型について

Objc では型については大きく分けて、値型と参照型の2つがあります。

Objc でいう参照型ってのは要はオブジェクトです。

参照型ってのはコピったときにメモリアドレスがはいる例のアレです。

Objc でいうと参照型であればもれなくオブジェクトだと思います(多分)

なので、メソッドが生えてたりプロパティを保持しています。

値型

※よく使うものに絞ります。

型名 意味 定義例
int 数値 int num = 3;
double 小数 double d = 123.45;
char 文字列 char str = "hoge";
BOOL 真偽値 BOOL b = YES;
NSInteger 数値
intとほぼ同じ(*)
NSInteger num = 3;

NSInteger とint との違いはこちら

参照型

※よく使うものに絞ります。

型名 意味 定義例
NSString 文字列 NSString *str = @"Hello";
NSMutableString 変更可能文字列 初期値なし : NSMutableString *str = [NSMutableString string];
初期値あり : NSMutableString *str = [NSMutableString stringWithString:@"Hoge"];
NSNumber 数値 NSNumber *num = [NSNumber numberWithInt:3];
NSArray 配列 最後にnil を入れるの注意
NSArray *array = [[NSArray alloc] initWithObjects:@"hoge", @"fuga", @"piyo", nil];
NSMutableArray 変更可能配列 初期値なし : NSMutableArray *array = [[NSMutableArray alloc] init];
初期値あり : NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@"aa", nil];
NSDictionary 辞書 初期値なし : NSDictionary *dic = [[NSDictionary alloc] init];
初期値あり :
NSArray *vals = [NSArray arrayWithObjects: @"val1", @"val2", nil];
NSArray *keys = [NSArray arrayWithObjects: @"key1", @"key2", nil];
NSDictionary *dic = [NSDictionary dictionaryWithObjects:vals forKeys:keys];

型キャスト

NSString => NSInteger

NSString *numStr = @"123";
NSInteger num = [numStr intValue];

NSString => NSNumber

NSString *numStr = @"123";
NSInteger num = [numStr intValue];
NSNumber *numNSNum = [NSNumber numberWithInt:num];

NSInteger => NSString

NSInteger num = 123;
NSString *str = [NSString stringWithFormat:@"%d", num];

NSNumber => NSInteger

NSNumber *nsNum = [NSNumber numberWithInt:3];
NSInteger nsInt = [num integerValue];

変数定義

上記の例をみてもらえれば分かるのですが、参照型は定義時にアスタリスク * をつける必要があります。

char cStr = "hoge"; //値型
NSString *nsStr = @"hoge"; //参照型

ヘッダーファイル、実装ファイル

ObjectiveC ではヘッダーファイル(.h)と 実装ファイル(.m)ってのがあります。

何が違うのかといいますと、ヘッダーファイルってのは実装ファイルのほうで使う変数やメソッドなどを定義します。

それに対して実装ファイルにはメソッドの処理内容などを記述します。

例(具体的なメソッド定義法などは下記参考):

TestLib.h

#ifndef TestLib_h
#define TestLib_h

@interface TestLib: NSObject;

// 書かないとプライベートメソッドになる
-(NSString *) sampleMethod;
-(void) sampleMethodWithArgs: (NSString *)hoge :(NSString *)fuga;
@end

#endif /* TestLib_h */

TestLib.m

#import <Foundation/Foundation.h>
#import "TestLib.h"

@interface TestLib ()

@end

@implementation TestLib

- (NSString *)sampleMethod {
    return @"Hello!";
}

- (void) sampleMethodWithArgs: (NSString *)hoge :(NSString *)fuga {
}

@end

メソッド定義

分かりづらいです。

引数なし

- (NSString *)sampleMethod {
    return @"Hello!";
}

引数あり(1つ)

- (void) sampleMethodWithArgs: (NSString *)hoge {
}

引数あり(2つ)

- (void) sampleMethodWithArgs: (NSString *)hoge :(NSString *)fuga {
}

ただ、Objc では第二引数以降はラベルをつけるのが一般的なようです。

参考 : 【Xcode】第二引数以降はラベルをつける【Objective-C】

- (void) sampleMethodWithArgs: (NSString *)hoge fuga:(NSString *)fuga {
}

メソッド実行

分かりづらいです。

以下の書式で行います。

引数なし

[obj method];

引数あり(1つ)

[obj method:arg];

引数あり(2つ)

[obj method:arg1 :arg2];

引数あり(2つ、ラベルあり)

[obj method:arg1 labelName:arg2];

デバッグ

基本的には Xcode上で break point を挟んで、po コマンドでOKです。

po.png (435.9 kB)

ただ、ビルドをするのに時間がかかるので、新しくメソッドを実装するときや複雑なロジックを書くときは僕はpaiza を使います。

Paiza.io

アウトプットを作るにはNSLog を使います。

#import <Foundation/Foundation.h>
int main(void){
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    // Your code here!
    NSMutableString *s = @"hoge";
    NSLog(@"%@", s);
    [pool release];
    return 0;
}

=> 2018-04-14 15:41:33.259 Main[10] hoge

クラスのimport

Xcode 上で試してみます。

Test プロジェクトの下に Library というディレクリを作成し、その下に TestLib.h, TestLib.m というファイルを作成しました。

test_dir.png (186.5 kB)

プロジェクトを作成すると生成される main.m に以下のように書きました。

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TestLib.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        id testLibObj;
        testLibObj = [[TestLib alloc] init];
        [testLibObj sampleMethod];
        [testLibObj sampleMethodWithArgs:@"hoge" fuga:@"fuga"];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

まぁこんな感じでimport & 使用できます。

名前空間はないです。

思想的には、名前の前に接頭辞をつければ衝突しないっしょ?っていうスタンスらしいです。

NSHoge みたいに。

文字列操作

文字追加

NSMutableString *str = [NSMutableString string];
[str appendString:@"HogeFuga"];
NSLog(@"%@", str);

=> HogeFuga

置換

NSString *str = @"HogeFuga";
NSString *replacedStr = [str stringByReplacingOccurrencesOfString:@"Hoge" withString:@"Hige"];
NSLog(@"%@", replacedStr);

=> HigeFuga

削除

NSString *str = @"HogeFuga";
NSString *removedStr1 = [str substringToIndex:4]; // 最初から指定されたインデックスの1文字前まで
NSString *removedStr2 = [str substringFromIndex:4]; // 指定されたインデックスから最後まで
NSString *removedStr3 = [str substringWithRange:NSMakeRange(2, 4)]; // 指定されたインデックスから文字数分
NSLog(@"%@", removedStr1);
NSLog(@"%@", removedStr2);
NSLog(@"%@", removedStr3);

=> Hoge

=> Fuga

=> geFu

配列操作

取得

NSArray *array = [[NSArray alloc] initWithObjects:@"hoge", @"fuga", @"piyo", nil];
NSLog(@"%@", [array objectAtIndex:0]); 

=> hoge

追加

NSMutableArray *array = [[NSMutableArray alloc] init]; 
[array addObject:@"Piyo"]; 
NSLog(@"%@", array]); 

=> (Piyo)

削除

NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@"hoge", @"piyo", nil]; 
[array removeObjectAtIndex:1]; 
NSLog(@"%@", array]); 

=> (hoge)

ソート

NSArray *array = [NSArray arrayWithObjects:[NSNumber numberWithInt:11],[NSNumber numberWithInt:1],[NSNumber numberWithInt:23],[NSNumber numberWithInt:4],[NSNumber numberWithInt:2],nil];
NSMutableArray *sortedArray = [array sortedArrayUsingDescriptors: @[[NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES]]];

=> (1, 2, 4, 11, 23)

※文字列だとうまくいかない

NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@"2", @"3", @"1", @"7", @"15", nil];
NSMutableArray *sortedArray = [array sortedArrayUsingDescriptors: @[[NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES]]];

=> (1, 15, 2, 3, 7)