読み込み中...

Objective-Cのクラス定義!初心者が必見の15選手法!

初心者のためのObjective-Cクラス定義ガイド Objctive-C
この記事は約26分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Objective-Cは、AppleのiOSやmacOSでのアプリケーション開発に多く使用されるプログラミング言語の1つです。

この言語の中でクラス定義は非常に重要な役割を果たしています。

今回の記事では、Objective-Cでのクラス定義に関する基本から応用、そして注意点までを徹底解説していきます。

初心者の方でも安心して読める内容となっておりますので、プログラミングの世界への第一歩として参考にしてください。

●Objective-Cとは

Objective-Cは、C言語をベースにしたオブジェクト指向のプログラミング言語であり、C++とは異なるアプローチでオブジェクト指向の概念を取り入れています。

特に、AppleのiOSやmacOSのアプリケーション開発で主流として使用されてきました。

○Objective-Cの概要

Objective-Cは、C言語の構文をベースに、Smalltalkからの影響を受けたオブジェクト指向の概念を持っています。

このため、C言語のコードとObjective-Cのコードを同じファイル内に混在させることができます。

Objective-Cは動的な言語であり、実行時に多くのことが決定されるため、柔軟性が高いと言われています。

○Objective-Cの歴史

Objective-Cは1980年代初頭にBrad CoxとTom Loveによって開発されました。

当初はNeXTコンピュータでの使用を目的としていましたが、後にAppleがNeXTを買収。

その結果、Objective-CはAppleの主要なプログラミング言語として採用されることとなりました。

iOSやmacOSのアプリケーション開発の際には、Objective-Cが中心的な役割を果たしてきましたが、近年ではSwiftという新しい言語も登場してきており、開発環境が大きく変わりつつあります。

Objective-Cが持つオブジェクト指向の概念や、それをどのように活用するのか、そしてどのような歴史的背景を持っているのかを理解することは、今後の学習の基盤となります。

特に初心者の方は、この部分をしっかりと把握しておくことをおすすめします。

●クラス定義の基本

プログラミング言語Objective-Cでのクラス定義を学ぶにあたり、最も基本となる概念を習得することは非常に重要です。

ここでは、Objective-Cのクラス定義の基本を掘り下げ、クラスやインスタンス、プロパティ、メソッドの基本概念について解説します。

○クラスとは

クラスは、オブジェクト指向プログラミングの中核的な存在であり、データの構造や、そのデータに対して行う操作をまとめたものを指します。

Objective-Cにおけるクラス定義は、データの形やその振る舞いを定義するためのテンプレートとなるものです。

具体的には、変数や関数をまとめて一つの単位として定義することができます。

○インスタンスとは

インスタンスは、定義されたクラスを実際にメモリ上に生成される実体のことを指します。

クラスは設計図のようなもので、その設計図に基づいて具体的な物を作成するのがインスタンスです。

Objective-Cにおいて、クラスからインスタンスを生成する際には、allocおよびinitメソッドを使用します。

これにより、クラスの定義に従ったオブジェクトがメモリ上に確保されるのです。

○プロパティとメソッドの定義

プロパティとは、クラス内で定義される変数のことを指し、そのクラスが持つデータを表現します。

Objective-Cにおけるプロパティの定義は、@propertyディレクティブを使用して行います。

これにより、外部からアクセス可能な変数を定義することができるようになります。

一方、メソッドは、クラス内で定義される関数のことを指し、そのクラスが持つ振る舞いや機能を表現します。

Objective-Cにおけるメソッドの定義は、- (戻り値の型)メソッド名:(引数の型)引数名という形式をとります。

●クラス定義の使い方

Objective-Cにおけるクラス定義の手法は多岐にわたりますが、ここではその基本的な使い方を2つのサンプルコードを交えて徹底的に解説します。

○サンプルコード1:基本的なクラス定義

Objective-Cでは、クラス定義は「@interface」と「@implementation」の二つの部分に分かれています。

「@interface」でクラスの宣言を、「@implementation」でその実装を行います。

// ファイル名: Person.h
@interface Person : NSObject
{
    NSString *name;
}
@end

// ファイル名: Person.m
@implementation Person
// ここにメソッドなどの実装を記述する
@end

このコードでは、名前を持つPersonというクラスを定義しています。

この例では、NSString型のnameというインスタンス変数を持つクラスを宣言しています。

○サンプルコード2:プロパティを持つクラス

Objective-Cでは、プロパティを使用して、外部からアクセス可能なインスタンス変数を簡単に定義することができます。

プロパティの定義は「@property」というキーワードを用いて行います。

// ファイル名: Student.h
@interface Student : NSObject
@property (nonatomic, strong) NSString *studentID;
@property (nonatomic, strong) NSString *name;
@end

// ファイル名: Student.m
@implementation Student
// プロパティの実装は自動的に行われるため、ここに特にコードを書く必要はありません。
@end

このコードでは、Studentというクラスを定義し、studentIDとnameという2つのプロパティを持つクラスを表しています。

この例では、学生のIDと名前を持つクラスを宣言しています。

プロパティを使用することで、外部からのアクセスが簡単になり、ゲッターやセッターの実装を省略することができます。

○サンプルコード3:メソッドを持つクラス

Objective-Cでは、メソッドはクラスの振る舞いを表す関数のようなものです。

メソッドは、そのクラスが持っているデータ(プロパティ)を操作したり、特定の機能を提供するためのものです。

ここでは、Objective-Cでメソッドを持つクラスを定義する基本的なサンプルコードを紹介します。

// Personクラスの定義
@interface Person : NSObject {
    NSString *name;
}

// メソッドの宣言
- (void)sayHello;

@end

@implementation Person

// メソッドの実装
- (void)sayHello {
    NSLog(@"Hello! My name is %@", name);
}

@end

このコードでは、Personというクラスを使って、sayHelloというメソッドを定義しています。

この例では、nameというプロパティにアクセスして、それを利用して挨拶のメッセージを表示します。

このコードを実行すると、sayHelloメソッドを呼び出すと、Hello! My name is [nameの値]というメッセージが表示されます。

ただし、この例ではnameの初期値が設定されていないため、具体的な名前は表示されません。

○サンプルコード4:イニシャライザの使用

クラスのインスタンスを作成する際に、初期設定を行うためのメソッドがイニシャライザです。

Objective-Cでは、イニシャライザは特定の命名規則に従って定義されます。

ここでは、イニシャライザを使用したサンプルコードを紹介します。

// Personクラスの定義
@interface Person : NSObject {
    NSString *name;
}

// イニシャライザの宣言
- (instancetype)initWithName:(NSString *)newName;

// メソッドの宣言
- (void)sayHello;

@end

@implementation Person

// イニシャライザの実装
- (instancetype)initWithName:(NSString *)newName {
    self = [super init];
    if (self) {
        name = newName;
    }
    return self;
}

// メソッドの実装
- (void)sayHello {
    NSLog(@"Hello! My name is %@", name);
}

@end

このコードでは、initWithName:というイニシャライザを定義しています。

このイニシャライザを使用してPersonクラスのインスタンスを作成する際に、名前を引数として与えることができます。

そして、sayHelloメソッドを呼び出すと、設定された名前で挨拶のメッセージが表示されます。

○サンプルコード5:クラスの継承

Objective-Cのクラス継承は、既存のクラスの機能を新しいクラスに引き継ぐことを可能にします。

これにより、既存のクラスの機能を再利用しながら新しい機能を追加することができます。

ここでは、クラス継承を使用したサンプルコードを紹介します。

// Personクラスの定義
@interface Person : NSObject {
    NSString *name;
}

- (instancetype)initWithName:(NSString *)newName;
- (void)sayHello;

@end

// Studentクラスの定義
@interface Student : Person {
    NSString *school;
}

- (instancetype)initWithName:(NSString *)newName andSchool:(NSString *)newSchool;
- (void)saySchool;

@end

@implementation Person

- (instancetype)initWithName:(NSString *)newName {
    self = [super init];
    if (self) {
        name = newName;
    }
    return self;
}

- (void)sayHello {
    NSLog(@"Hello! My name is %@", name);
}

@end

@implementation Student

- (instancetype)initWithName:(NSString *)newName andSchool:(NSString *)newSchool {
    self = [super initWithName:newName];
    if (self) {
        school = newSchool;
    }
    return self;
}

- (void)saySchool {
    NSLog(@"I go to %@", school);
}

@end

このコードでは、Personクラスを基にして、Studentという新しいクラスを定義しています。

StudentクラスはPersonクラスの機能を継承しているため、sayHelloメソッドを利用することができます。

また、新しくsaySchoolというメソッドも追加しています。

このコードを実行すると、Studentクラスのインスタンスを作成し、sayHellosaySchoolの両方のメソッドを呼び出すことができます。

●クラス定義の応用例

Objective-Cでのクラス定義は、基本的な使い方だけでなく、さまざまな応用的な使い方もあります。

ここでは、プロトコルの導入やカテゴリを使った拡張など、クラス定義の応用例について紹介します。

○サンプルコード6:プロトコルの導入

プロトコルは、Objective-Cでのインターフェースを定義するためのものです。

クラスがプロトコルを採用すると、そのプロトコルに定義されているメソッドの実装を保証することが求められます。

#import <Foundation/Foundation.h>

// プロトコルの定義
@protocol SampleProtocol
- (void)sampleMethod;
@end

// クラス定義とプロトコルの採用
@interface MyClass : NSObject <SampleProtocol>
@end

@implementation MyClass
// プロトコルで宣言されたメソッドの実装
- (void)sampleMethod {
    NSLog(@"プロトコルのメソッドが呼ばれました。");
}
@end

このコードでは、SampleProtocolという名前のプロトコルを定義し、その中でsampleMethodというメソッドを宣言しています。

MyClassというクラスは、このプロトコルを採用しているため、sampleMethodメソッドの実装が必須となります。実際にMyClassの中でsampleMethodを実装していることが確認できます。

この例のように、プロトコルを使うことで、特定のメソッドの実装を強制することができ、コードの安全性や再利用性を高めることができます。

このコードを実行すると、”プロトコルのメソッドが呼ばれました。”というメッセージがコンソールに出力されます。

○サンプルコード7:カテゴリを使った拡張

Objective-Cでは、カテゴリを使用して既存のクラスにメソッドを追加することができます。

これにより、ライブラリやフレームワークのクラスを拡張する際に役立ちます。

#import <Foundation/Foundation.h>

// NSStringクラスにカテゴリを追加
@interface NSString (MyCategory)
- (NSString *)reversedString;
@end

@implementation NSString (MyCategory)
- (NSString *)reversedString {
    NSMutableString *reversed = [NSMutableString stringWithCapacity:[self length]];
    for (NSInteger i = [self length] - 1; i >= 0; i--) {
        [reversed appendFormat:@"%c", [self characterAtIndex:i]];
    }
    return reversed;
}
@end

このコードでは、NSStringクラスにreversedStringというメソッドを追加しています。

このメソッドは、文字列を逆順にする機能を持っています。

カテゴリを使うことで、既存のクラスに新しいメソッドを追加することが容易になり、コードの再利用性が向上します。

このコードを使用して、”hello”という文字列を逆順にする場合、”olleh”という結果が得られます。

○サンプルコード8:コンポジションによる機能の組み合わせ

Objective-Cでは、あるクラスの機能を別のクラスに組み込むための方法として「コンポジション」という手法が存在します。

継承ではなく、一つのクラスが別のクラスのオブジェクトを保持することで、それらのクラスの機能を利用することができるのです。

このコードでは、CarクラスとEngineクラスを使ってコンポジションの例を表しています。

この例では、CarクラスがEngineクラスのオブジェクトを保持し、エンジンの機能を利用しています。

@interface Engine : NSObject
- (void)start;
@end

@implementation Engine
- (void)start {
    NSLog(@"エンジンを起動します。");
}
@end

@interface Car : NSObject {
    Engine *engine;
}
- (id)initWithEngine:(Engine *)eng;
- (void)startCar;
@end

@implementation Car
- (id)initWithEngine:(Engine *)eng {
    self = [super init];
    if (self) {
        engine = eng;
    }
    return self;
}

- (void)startCar {
    [engine start];
    NSLog(@"車を走らせます。");
}
@end

int main() {
    Engine *myEngine = [[Engine alloc] init];
    Car *myCar = [[Car alloc] initWithEngine:myEngine];
    [myCar startCar];
    return 0;
}

上記のコードを実行すると、「エンジンを起動します。」「車を走らせます。」という出力が得られます。

これにより、CarクラスはEngineクラスの機能を持つことができるわけです。

○サンプルコード9:ブロックを利用したコールバック

Objective-Cには「ブロック」という機能が存在し、関数のようにコードの塊をオブジェクトとして扱うことができます。

このブロックは、コールバックや非同期処理の実装など様々な場面で有効活用されます。

このコードでは、ブロックを使って非同期の処理完了時にコールバックを行う例を表しています。

この例では、あるタスクを非同期に実行し、完了後にブロックを呼び出しています。

typedef void (^CompletionBlock)(NSString *result);

@interface TaskRunner : NSObject
- (void)runTaskWithCompletion:(CompletionBlock)block;
@end

@implementation TaskRunner
- (void)runTaskWithCompletion:(CompletionBlock)block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 何らかの処理
        NSString *result = @"タスク完了!";
        block(result);
    });
}
@end

int main() {
    TaskRunner *runner = [[TaskRunner alloc] init];
    [runner runTaskWithCompletion:^(NSString *result) {
        NSLog(@"%@", result);
    }];
    return 0;
}

このコードを実行すると、「タスク完了!」という出力が非同期に表示されます。

これにより、非同期の処理結果を取得して後続の処理を行うことができるわけです。

○サンプルコード10:動的メソッド解決

Objective-Cの特徴の一つに、メソッドの動的解決が挙げられます。

これは、メソッドが実際に呼び出される際に、そのメソッドの実装を探し出して実行する機能です。

このコードでは、動的にメソッドを解決する例を表しています。

この例では、メソッドが存在しない場合に、動的にそのメソッドの実装を追加しています。

#import <objc/runtime.h>

@interface DynamicClass : NSObject
@end

@implementation DynamicClass
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(dynamicMethod)) {
        class_addMethod([self class], sel, (IMP)dynamicImp, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void dynamicImp(id self, SEL _cmd) {
    NSLog(@"動的に解決されたメソッドです!");
}
@end

int main() {
    DynamicClass *obj = [[DynamicClass alloc] init];
    [obj performSelector:@selector(dynamicMethod)];
    return 0;
}

このコードを実行すると、「動的に解決されたメソッドです!」という出力が得られます。

これにより、メソッドが存在しない場合でも、その場で実装を追加して呼び出すことができるのです。

●注意点と対処法

Objective-Cを学ぶ中で、初心者がつまずくことが多い注意点とその対処法を解説します。

詳細なサンプルコードを交えて、一つ一つの問題点とその解決法を理解していきましょう。

○メモリ管理とARC

Objective-Cにおけるメモリ管理は、特に初心者にとっては難しい部分の一つです。

従来のObjective-Cでは手動でのリファレンスカウントを用いたメモリ管理が主流でしたが、現代では「Automatic Reference Counting(ARC)」という技術が導入され、大幅にメモリ管理が簡単になりました。

このコードではARCを用いたメモリ管理を表しています。

この例では、新たにオブジェクトを生成し、そのオブジェクトのメモリ管理がARCによって自動的に行われる様子を表しています。

// Personクラスの定義
@interface Person : NSObject
@property (strong, nonatomic) NSString *name;
@end

@implementation Person
@end

// インスタンスの生成
Person *person = [[Person alloc] init];
person.name = @"Taro";

このサンプルコードを実行すると、Personクラスのインスタンスが生成され、nameプロパティに”Taro”が代入されます。

このインスタンスは、利用が終わった後、ARCによって自動的にメモリから解放されます。

○非互換性の問題への対処

Objective-Cは長い歴史を持つ言語であり、バージョンアップに伴い非互換性が生じることがあります。

特に、古いコードと新しいフレームワークやライブラリを組み合わせる際には注意が必要です。

このコードでは、非互換性の問題を回避するための条件分岐を表しています。

この例では、あるメソッドが存在するかどうかを確認し、存在する場合にのみそのメソッドを呼び出すという処理を行っています。

if ([object respondsToSelector:@selector(someMethod)]) {
    [object someMethod];
}

このサンプルコードを実行すると、objectにsomeMethodというメソッドが存在する場合のみ、そのメソッドが呼び出されます。

これにより、古いコードと新しいフレームワークやライブラリとの非互換性を避けることができます。

○パフォーマンスに関する注意点

Objective-Cのプログラムを効率よく動作させるためには、パフォーマンスに関するいくつかの注意点を理解しておくことが重要です。

例えば、ループの中で不必要なオブジェクトの生成を繰り返すと、メモリ使用量が増加し、アプリケーションの動作が遅くなる可能性があります。

このコードでは、ループの外でオブジェクトの生成を行い、ループの中でそのオブジェクトを利用する方法を表しています。

この例では、NSArrayのインスタンスを生成し、その中の要素を順番に取り出して処理を行っています。

NSArray *items = @[@"apple", @"banana", @"cherry"];
NSString *item;
for (int i = 0; i < [items count]; i++) {
    item = items[i];
    NSLog(@"%@", item);
}

このサンプルコードを実行すると、itemsの中の要素が順番に取り出され、ログに表示されます。

ループの外でオブジェクトの生成を行うことにより、メモリ使用量を削減し、アプリケーションの動作を高速化することができます。

●カスタマイズ方法

Objective-Cにおけるクラス定義のカスタマイズは、個別のプロジェクトの要件に合わせて、機能や設計を調整することができます。

特定のデザインパターンの採用や、ライブラリの活用により、より効率的かつ簡潔なコードを記述することが可能となります。

○サンプルコード11:デザインパターンの導入

デザインパターンは、特定の問題を解決するための設計上のベストプラクティスです。

Objective-Cでのクラス定義においても、デザインパターンの採用は非常に有効です。

ここでは、シングルトンパターンを導入したサンプルコードを紹介します。

@interface SingletonClass : NSObject

+ (instancetype)sharedInstance;

@end

@implementation SingletonClass

static SingletonClass *singletonInstance = nil;

+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singletonInstance = [[self alloc] init];
    });
    return singletonInstance;
}

@end

このコードでは、SingletonClassという名前のクラスをシングルトンパターンで実装しています。

+ sharedInstanceメソッドを通じて、同じインスタンスにアクセスすることができるようになっています。

この例では、dispatch_once関数を使用してスレッドセーフなシングルトンの生成を保証しています。

このコードを利用すると、次のようにインスタンスにアクセスできます。

SingletonClass *singleton = [SingletonClass sharedInstance];

○サンプルコード12:ライブラリの活用

Objective-Cの開発において、外部ライブラリやフレームワークの活用は、多くの機能を迅速に実装するためのショートカットとなります。

ここでは、Objective-Cで人気のあるライブラリ「AFNetworking」を使用して、HTTPリクエストを行うサンプルコードです。

#import "AFNetworking.h"

// HTTPリクエストを行う
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:@"https://api.example.com/data"
  parameters:nil
     success:^(NSURLSessionTask *task, id responseObject) {
         NSLog(@"JSON: %@", responseObject);
     } failure:^(NSURLSessionTask *operation, NSError *error) {
         NSLog(@"Error: %@", error);
     }];

このコードでは、AFNetworkingライブラリを利用して、指定されたURLからデータを非同期に取得しています。

この例では、GETリクエストを行い、成功時にはJSONレスポンスをログに出力し、失敗時にはエラー情報をログに出力しています。

AFNetworkingの導入により、通常のNSURLConnectionやNSURLSessionを使用するよりも短いコードで非同期のネットワーク処理を行うことができます。

○サンプルコード13:外部ツールの連携

Objective-Cは長い歴史を持つ言語のため、様々な外部ツールとの連携が可能です。

ここでは、Objective-Cのプロジェクトと外部ツールを連携させる手法を解説します。

このコードでは、Objective-Cから外部ツールを呼び出してデータを取得するコードを表しています。

この例では、system関数を使用してコマンドラインツールを呼び出し、その結果をObjective-Cの変数に格納しています。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 外部ツールのコマンドラインを実行
        system("ls");
    }
    return 0;
}

上記のコードを実行すると、カレントディレクトリのファイル一覧が表示されます。

Objective-Cのプログラム内から直接、シェルコマンドを実行することが確認できるでしょう。

○サンプルコード14:スクリプト言語との連携

Objective-CはC言語ベースのため、他のスクリプト言語との連携も容易です。

ここでは、Pythonとの連携を例に挙げます。

このコードでは、Objective-CからPythonスクリプトを呼び出し、その結果を取得する手法を表しています。

この例では、Pythonスクリプトを実行し、その結果をObjective-Cの変数に格納しています。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *pythonScript = @"/path/to/your/python/script.py";
        NSString *result = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:pythonScript]
                                                    encoding:NSUTF8StringEncoding
                                                       error:NULL];
        NSLog(@"%@", result);
    }
    return 0;
}

上記のコードでは、指定したPythonスクリプトの出力結果をresultという変数に格納し、ログに出力しています。

このように、Objective-Cと他のスクリプト言語との連携を行うことができます。

○サンプルコード15:モジュールの利用

Objective-Cでの開発を効率的に進めるためには、モジュールの利用が欠かせません。

モジュールとは、あらかじめ定義された機能やクラスをまとめたもので、再利用性を高めるために使用されます。

このコードでは、Objective-Cで外部モジュールを読み込む手法を表しています。

この例では、JSONデータを扱うためのモジュールを読み込んで使用しています。

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *jsonString = @"{\"key\": \"value\"}";
        NSDictionary *jsonDictionary = [jsonString objectFromJSONString];
        NSLog(@"%@", jsonDictionary);
    }
    return 0;
}

上記のコードを実行すると、JSON形式の文字列をNSDictionaryの形式に変換してログに出力します。

このように、Objective-Cでは外部モジュールを使用することで、開発の効率を大幅に向上させることが可能です。

まとめ

Objective-Cにおけるクラス定義の手法は、基本的なものから応用までさまざまです。

今回紹介した内容を通して、外部ツールやスクリプト言語との連携、モジュールの利用といった高度な技術も取り入れながら、より効率的なプログラムの実装が可能であることを理解できたことでしょう。

初心者の方もこれらの手法を学ぶことで、Objective-Cのプログラミングの幅を広げる一助となることを願っています。

プログラミングの世界は深く、絶えず新しい知識や技術が求められますが、基本をしっかりと理解し、それをベースにスキルアップを図っていくことで、より高度なプログラムを効果的に実装することができるでしょう。