Objective-Cのnonnull理解度5倍アップの方法10選

Objective-Cのコード例とともに、nonnull属性の説明をしている画面Objctive-C
この記事は約23分で読めます。

※本記事のコンテンツは、利用目的を問わずご活用いただけます。実務経験10000時間以上のエンジニアが監修しており、基礎知識があれば初心者にも理解していただけるように、常に解説内容のわかりやすさや記事の品質に注力しております。不具合・分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。(理解できない部分などの個別相談も無償で承っております)
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)


はじめに

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

C言語にオブジェクト指向の概念をプラスした形で設計されており、その特性から多くの開発者に利用されています。

この記事ではObjective-Cの基本をおさらいした後、特にnonnull属性の理解を深め、その活用法を詳しく解説していきます。

●Objective-Cとは

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

C言語の上にSmalltalkのオブジェクト指向機能を組み込む形で作られ、Appleによってその後大きく普及しました。

Objective-Cは、その後Swiftに取って代わられるまで、Appleのオペレーティングシステムの開発の中心言語でした。

○Objective-Cの歴史と特徴

Objective-Cが初めて公にリリースされたのは1983年のことで、その後NeXTの手によって開発が進められました。

AppleがNeXTを買収したことにより、Objective-CはmacOSやiOSといったAppleの製品の核となる開発言語になりました。

この言語の大きな特徴は、動的なタイピングをサポートすることであり、開発者が実行時に多くの決定を下せる柔軟性を持っています。

○Objective-Cの基本構文

Objective-Cの構文はC言語にオブジェクト指向の概念を加えたもので、C言語のすべての機能に加えて、クラスの継承、ポリモーフィズム、カプセル化などの機能を備えています。

Objective-Cではメッセージ送信に角括弧([])を使用し、クラスの宣言や実装には@interfaceと@implementationというキーワードを使用します。

また、プロトコルと呼ばれるインターフェースの宣言により、どのクラスも特定のメソッド群の実装を保証することができます。

●nonnull属性とは

Objective-Cにおけるnonnull属性は、開発者がnull非許容のパラメーターやプロパティを明示するために用います。

これはObjective-Cプログラミング言語の安全性を強化する上で重要な役割を担っており、APIの設計においてnull値が意図されていない場所でのnullの使用を防ぐことを目的としています。

nonnull属性を使用することで、コンパイラはそのパラメーターまたは戻り値がnilでないことを確認する追加のチェックを行います。

これにより、開発者はランタイムエラーを減らし、より堅牢なコードを作成することができます。

○nonnull属性の目的と概要

nonnull属性の主な目的は、関数やメソッドの引数、戻り値、プロパティがnilを受け取らない、または返さないことを保証することです。

この属性を使うことで、開発者はnull許容性をコードの契約として明示的に定義でき、それによりエラーの可能性を低下させ、プログラムの信頼性を高めることができます。

例えば、メソッドの引数が必ず必要である場合や、初期化後にプロパティが常に有効な値を持つことを保証する場面で有用です。

○nonnullとnullableの違い

nonnullとnullableはObjective-Cにおけるnull許容性アノテーションです。

ここで「nonnull」はその変数やパラメータがnilを取り得ないことを表し、「nullable」はnilを取り得ることを表します。

これらの属性を使用することで、コードの意図がより明確になり、開発者が意図しないnullによるエラーを防ぐ助けになります。

例えば、あるメソッドがnilを受け入れる場合、nullableを使用し、そうでない場合はnonnullを使用します。

これらの違いを理解し適切に使用することで、より安全で保守しやすいコードを書くことができます。

●Objective-Cのnonnull属性の基本的な使い方

Objective-Cでは、nonnull属性を使うことで、プログラマーが意図しないnullポインタの割り当てを防ぎ、プログラムの安全性を高めることができます。

nonnull属性は、変数やパラメータがnullであってはならないことを明示するために用いられます。

これにより、コンパイラによるチェックが強化され、開発者が安全なコードを書くのに役立ちます。

nonnull属性を用いる一番一般的なシナリオは、メソッドのパラメータや戻り値に対してnullが許容されないことを明示する場合です。

また、プロパティを宣言する際にも、そのプロパティがnullになることがないと保証するためにnonnullを使用します。

Objective-Cでは、_Nonnullまたはnonnullのキーワードを使用してこの属性を指定できます。属性を使わない場合、パラメータや戻り値がnullになる可能性があるため、ランタイムエラーや不具合の原因になりかねません。

非null制約をコードに適用することで、より堅牢なアプリケーションを作成することが可能になります。

○サンプルコード1:メソッド宣言にnonnullを使用する

Objective-Cのメソッド宣言においてnonnullを使用する例を見てみましょう。

下記のコードでは、カスタムクラスのメソッドがnullでない引数を受け取るという契約をコンパイラに知らせます。

- (void)configureViewWithModel:(nonnull MyModel *)model {
    // このコードではmodelパラメータが非nullであることを期待しています。
    // これにより、modelがnullである場合の不具合を防ぎます。
    // modelを使用してビューを設定しています。
    [self setupWithModel:model];
}

この例ではconfigureViewWithModelメソッドが非nullのMyModelインスタンスを受け取り、それを使ってビューの設定を行っています。

nonnull属性がなければ、modelがnullであるリスクがあり、メソッド内でmodelにアクセスする際にアプリケーションがクラッシュする可能性があります。

○サンプルコード2:プロパティ宣言にnonnullを使用する

次に、クラスのプロパティを宣言する際にnonnull属性を使用する方法を見ていきます。

下記のコードでは、MyViewControllerクラスの重要なプロパティが常にnullでないことを保証しています。

@interface MyViewController : UIViewController

@property (nonatomic, strong, nonnull) MyModel *model;

@end

このコードではMyViewControllerクラスにmodelプロパティがあり、このプロパティが非nullであることを表しています。

nonatomicとstrongはプロパティの属性で、nonatomicはスレッドセーフでないことを示し、strongはプロパティが所有権を保持することを意味しています。

nonnullの指定により、modelプロパティには有効なMyModelインスタンスが常に関連付けられることが保証されます。

●nonnullの詳細な使用法

Objective-Cでの安全なプログラミングを行う上で、nonnull属性の理解と適切な使用は非常に重要です。

nonnullは、プロパティやメソッドのパラメータ、戻り値がnilではないことを明示するために使用されます。

この属性を用いることで、コンパイラによるチェックが強化され、実行時に想定外のnilが参照されることによるバグを事前に防ぐことができます。

Objective-Cでは、null許容性のアノテーションには主にnonnullとnullableがあります。

これらのキーワードを使うことで、コードの意図をはっきりと示し、APIの使用方法を文書化することなくコード自体で明示することができます。

たとえば、メソッドの引数や戻り値で、開発者がnilを期待していない場合にはnonnullを使用すると、コンパイラがnilの代入を警告として報告します。

これにより、nullポインタ例外を避けるための追加の安全性をプログラムにもたらすことができます。

○サンプルコード3:initメソッドとnonnull

Objective-Cのinitメソッドをnonnullと共に使用する例を見てみましょう。

通常、初期化メソッドはオブジェクトが適切に初期化されたことを保証するために使用されますが、nonnull属性を加えることで、この初期化がnilを返さないことをコンパイラに明示することができます。

@interface MyClass : NSObject

- (instancetype _Nonnull)initWithValue:(NSString * _Nonnull)value;

@end

@implementation MyClass

- (instancetype)initWithValue:(NSString * _Nonnull)value {
    self = [super init];
    if (self) {
        // 初期化のプロセスを行う
        // valueがnilでないことを確認するのは呼び出し側の責任です。
        _value = [value copy];
    }
    return self;
}

@end

このコードでは、MyClassのinitWithValueメソッドが引数として受け取るvalueに対してnonnullを使用しています。

これにより、value引数にnilが渡された場合、コンパイラは警告を発することで、開発者が注意を払うようになります。

同様に、initWithValueメソッド自体もnonnullが指定されており、これがnilを返さないことを保証しています。

この初期化メソッドの使用により、MyClassのインスタンスがnilでない状態で生成され、そのプロパティにも安全にアクセスできることが期待されます。

これは特に、コレクションクラスや他のクラスとの連携が重要な場面で有効です。

○サンプルコード4:エラーハンドリングとnonnull

エラーハンドリングを行うメソッドでは、エラーオブジェクトを参照するポインタをメソッドのパラメータとして受け取ります。

nonnullを使用して、メソッドの呼び出し側がエラーハンドリングを強制できるようにすることも可能です。

- (BOOL)performTaskWithError:(NSError * _Nonnull * _Nonnull)error {
    if (error == NULL) {
        // エラーへのポインタがnilの場合、早期リターンします。
        return NO;
    }

    // タスクが成功したかどうかを判断するロジック
    BOOL taskSuccess = YES; // 仮定のロジック
    if (!taskSuccess) {
        // エラー情報を作成し、errorに割り当てます。
        *error = [NSError errorWithDomain:@"com.example.MyErrorDomain" code:1001 userInfo:nil];
    }
    return taskSuccess;
}

このコードでは、performTaskWithError:メソッドが成功した場合はYESを返し、失敗した場合にはNOを返し、同時にエラー情報を渡すようになっています。

メソッドの引数であるerrorには二重のnonnullが使用されており、これはエラー情報を受け取るためのポインタ自体がnilであってはならず、さらにそのポインタが指し示す場所もnilであってはならないことを表しています。

この手法により、エラーが発生した際には確実に適切なエラーハンドリングが行われるように強制することができます。

開発者はこの情報をもとに、ユーザーへの適切なフィードバックやロギング、リトライロジックを実装することができます。

○サンプルコード5:カスタムクラスとnonnull

カスタムクラスを作成する際にも、nonnullとnullableの指定を明確に行うことで、より安全なコードを実現できます。

たとえば、特定のカスタムクラスのプロパティについて、nilの設定を許容しない場合は次のように宣言します。

@interface CustomObject : NSObject

@property (nonatomic, strong, nonnull) NSString *mandatoryString;

@end

@implementation CustomObject

- (instancetype)initWithMandatoryString:(NSString * _Nonnull)string {
    self = [super init];
    if (self) {
        _mandatoryString = string;
    }
    return self;
}

@end

この例では、CustomObjectのmandatoryStringプロパティがnilでないことが保証されています。

これにより、このプロパティを使用する際の安全性が向上し、ランタイムエラーを減少させることができます。

●nonnullを使ったエラーの防止

Objective-Cで安全なプログラミングを行うには、nonnullを活用してnilが割り当てられないようにすることが一つの鍵となります。

これは、オプショナルではない値が必要な場所でnilの割り当てがなされると予期しないクラッシュやバグが発生するためです。

nonnullを指定することで、コンパイラがその変数やプロパティに対してnullを許容しないことを保証し、開発者がより堅牢なコードを書く手助けをします。

○サンプルコード6:非null保証をする関数の実装

Objective-Cでは、関数やメソッドの定義において、引数や戻り値がnilであってはならないことを明示するためにnonnullを使用します。

下記のサンプルコードは、非nullの文字列を受け取って処理する関数の実装例を表しています。

// この関数は非nullの文字列のみを受け付けることを保証する
- (void)processMandatoryString:(NSString * _Nonnull)mandatoryString {
    // 文字列を使用した処理を実行する
    NSLog(@"Received string: %@", mandatoryString);
}

このコードではprocessMandatoryString:メソッドを使ってNSStringを処理しています。

この例ではmandatoryStringパラメータにnonnull属性を指定しているため、このメソッドを呼び出す際にnilを渡すことはできません。

もしnilを渡すと、コンパイル時に警告またはエラーが出るため、誤ってnullを渡すリスクを減らすことができます。

このメソッドを正しく実行すると、コンソールに「Received string:」と受け取った文字列が出力されます。

これは、nonnullを適切に使用することで安全な値のやり取りを保証している良い例です。

○サンプルコード7:null可能性のあるパラメータの検証

時には、nullの可能性があるパラメータを受け取る場合もあります。

そのような場合には、nullチェックを明示的に行う必要があります。

下記のコードは、パラメータがnullである可能性がある場合に検証を行う方法を表しています。

// このメソッドはnullの可能性があるパラメータを検証する
- (void)validateParameter:(NSString * _Nullable)parameter {
    if (parameter != nil) {
        // parameterがnilでない場合にのみ処理を行う
        NSLog(@"Parameter is valid: %@", parameter);
    } else {
        // parameterがnilの場合の処理を行う
        NSLog(@"Parameter is null and cannot be processed.");
    }
}

このコードでは、validateParameter:メソッドがnullableなNSStringパラメータを受け取り、その値をチェックしています。

nilでない場合には有効な処理が行われ、nilの場合には適切なメッセージがログに出力されます。

この例から、null許容パラメータを受け取る際には、必ずその値の検証を行うことの重要性がわかります。

これにより、アプリケーションの安定性を保ちながら、柔軟なパラメータの受け入れが可能になります。

●nonnull属性の応用例

プログラミング言語Objective-Cでは、nonnull属性を利用することでポインタ型のパラメータや戻り値がnilでないことを明示できます。

これはコンパイラによるチェックを強化し、プログラムの安全性を高めるのに役立ちます。

特に大規模なプロジェクトやチームでの開発において、このような明示はバグを減少させ、保守性を向上させるために非常に有効です。

○サンプルコード8:データベースクエリとnonnull

データベースからのクエリ結果を処理する際にnonnull属性を使用することで、返されるオブジェクトがnilでないことを保証することができます。

ここでは、データベースからのデータ取得メソッドにnonnullを適用した例を紹介します。

// データベースからユーザ情報を取得して、ユーザオブジェクトを返すメソッド
- (nonnull User *)fetchUserWithID:(nonnull NSString *)userID {
    User *user = [self.database executeQueryForUserWithID:userID];
    NSAssert(user != nil, @"User object must not be nil");
    return user;
}

このコードではexecuteQueryForUserWithID:メソッドを使ってユーザーIDに基づくデータをデータベースから取得し、結果としてUserオブジェクトを返しています。

この例では、メソッドの戻り値とパラメータにnonnullを指定し、NSAssert関数を使用して実行時にもnilでないことを検証しています。

これにより、ユーザーIDが正しいという前提で安全にメソッドを利用できます。

実際の開発では、この方法でデータの整合性を保つことが可能となり、データベースのクエリが想定通りにnilを返さないことを確認できます。

○サンプルコード9:API呼び出しとnonnull

APIを介してデータを取得する際も、nonnull属性を使用することで、受け取ったデータがnilでないことを保証することができます。

例えば、Webサービスからのレスポンスデータをパースするメソッドにnonnullを適用したコードは次のようになります。

// Webサービスからデータを取得し、パースするメソッド
- (nonnull NSDictionary *)fetchAndParseDataFromService:(nonnull NSURL *)serviceURL {
    NSData *responseData = [NSData dataWithContentsOfURL:serviceURL];
    NSError *error;
    NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
    NSAssert(parsedData != nil, @"Parsed data dictionary must not be nil");
    return parsedData;
}

このコードでは、指定されたURLからデータを取得し、JSONとしてパースしてNSDictionaryオブジェクトを返しています。

ここでも、戻り値としてnonnullを指定し、エラー発生時にはNSAssertを使ってnilでないことを確認しています。

これにより、APIからのレスポンスを安全に扱い、プログラムの堅牢性を向上させることができます。

○サンプルコード10:ユーザー入力の検証とnonnull

ユーザーからの入力を扱う際には、入力値がnilでないことを保証することが重要です。

nonnull属性を利用することで、ユーザー入力の検証処理を強化することが可能です。

次のコードスニペットは、テキストフィールドからの入力値を検証するメソッドの一例です。

// テキストフィールドの入力値を検証するメソッド
- (void)validateTextFieldInput:(nonnull UITextField *)textField {
    NSString *inputText = textField.text;
    NSAssert(inputText != nil, @"Input text must not be nil");
    // 入力値の検証ロジックを実行する...
}

ここでのポイントは、メソッドの引数として渡されるUITextFieldオブジェクトがnilでないこと、そしてtextFieldのtextプロパティがnilでないことを保証するためにnonnull属性とアサーションを使用していることです。

このようにして、ユーザー入力の処理をより安全に行うことができます。

●注意点と対処法

Objective-Cでプログラミングを行う上で非常に重要なのが、データのnull許容性を適切に管理することです。

nullとは「何もない」を意味し、ポインタが何も指していない状態を指します。

これを誤って扱うことで、プログラムはクラッシュしたり、予期しない動作をする原因となります。

ここでは、nullに関連するバグを避けるための注意点とその対処法について具体的な例を挙げながら解説します。

○Objective-Cにおけるnull関連のバグを避ける

Objective-Cにおいてnullは、オブジェクトが存在しないことを示すため、しばしば問題の源となります。

コード内でnullを適切に扱うためには、nonnullおよびnullableキーワードの適切な使用が鍵となります。

nonnullキーワードは、メソッドやプロパティがnullを受け入れないことをコンパイラに表します。

これにより、開発者は意図しないnullの使用を防ぎ、より安全なコードを記述することができます。

下記のコード例は、Objective-Cでnonnullキーワードを使用してメソッドを宣言する一般的な方法を表しています。

// ヘッダーファイル
@interface MyClass : NSObject

- (void)myMethodWithNonNullParam:(NSString * _Nonnull)param;

@end

このコードでは、myMethodWithNonNullParam: メソッドは非nullのNSStringオブジェクトをパラメータとして受け取ります。

このメソッドを使用する際にnullを渡すと、コンパイル時に警告が生成され、バグの発生を未然に防ぐことができます。

実装ファイルでは次のように記述します。

// 実装ファイル
@implementation MyClass

- (void)myMethodWithNonNullParam:(NSString * _Nonnull)param {
    // 引数 param が null でないことを保証するための実装
    NSLog(@"Received non-null string: %@", param);
}

@end

この例では、myMethodWithNonNullParam: メソッドがnullでないNSStringを受け取り、ログにその内容を出力しています。

もしnullを渡した場合、コンパイラが警告を出すことで、開発者がエラーに気づくことができます。

さらに、このメソッドの実行時にnullが渡された場合の動作についても考慮する必要があります。

nullを許容しないパラメータに対してnullを渡した場合、Objective-Cランタイムは例外を投げ、アプリケーションがクラッシュする可能性があるためです。

○nonnull使用時の一般的なエラーとその解決策

Objective-Cでnonnullを使用する際によくあるエラーは、nonnullパラメータや戻り値にnullを誤って渡すことです。

これは主に、APIの契約を誤解しているか、または不注意によるものであり、コードレビュー、適切なテストケースの実装、およびnull許容性の明確なドキュメントによって防ぐことができます。

開発者は常に、関数やメソッドの契約を十分に理解し、それに従ってnullを適切に扱うべきです。

もしnullが渡される可能性がある場合は、対応する処理を実装するか、nullが渡されないような設計に変更することが求められます。

エラーハンドリングの実装も重要です。

Objective-Cでは、例外を投げることでランタイムエラーを生成することができますが、それによってアプリケーションがクラッシュするのを避けるためには、エラーが起こりうる箇所での適切な処理が不可欠です。

また、nilを受け取り得るメソッドの呼び出しでは、戻り値を検証する習慣をつけることも大切です。

●カスタマイズ方法

プログラムの信頼性を高めるために、Objective-Cではnullを許容するかどうかをコードレベルで指定することができます。

これにより、プログラマーはメモリ管理やパラメータの扱いに関してより詳細な制御を行うことができます。

しかし、プロジェクトごとに異なる要件があるため、Objective-Cのnonnull属性をカスタマイズして使用する必要があります。

カスタマイズを行う際には、プロジェクトの目的や設計、チームの規約に沿った方法を選択することが重要です。

非nullポリシーのカスタマイズには、プロジェクトの安全性を高めるために、すべての関数やメソッドで引数や戻り値に対するnullチェックを徹底することが含まれます。

これは、意図しないnullの使用によるエラーを未然に防ぐための戦略として非常に有効です。

プロジェクト固有のnonnullルールの設定では、各開発者がプロジェクトの規範に従ってコードを記述しやすくなります。

プロジェクトのドキュメントに明記し、新しいメンバーがチームに参加するたびにこれらのルールを教育するプロセスを確立することが不可欠です。

また、コードの保守性を高めるために、プロジェクト内のnonnull使用に関する統一されたスタイルガイドを作成し、チームメンバー全員でそのガイドラインに従うことが推奨されます。

次に、これらのカスタマイズ方法を具体的なコーディングの場面でどのように適用するか見ていきます。

○非nullポリシーのカスタマイズ

Objective-Cで非nullポリシーをカスタマイズするために、ある関数がnullを許容しないことを明示するには、関数宣言においてnonnull属性を使用します。

例えば、文字列を処理する関数でnull許容を禁止したい場合、次のように宣言することができます。

- (void)processString:(NSString * _Nonnull)string {
    // 文字列の処理をここに記述
}

このコードでは、processString:メソッドはstringパラメータとしてnullを受け取らないことを保証しています。

このメソッドを使用する際には、渡す文字列がnullでないことを確認するか、nullである可能性がある場合は別のロジックを実装する必要があります。

○プロジェクト固有のnonnullルールの設定

プロジェクトの各メンバーがnull許容に関する同一の理解を持つことは、コードの一貫性と信頼性を保つ上で非常に重要です。

プロジェクト固有のルールを設定する際には、以下のような指針を考慮すると良いでしょう。

  1. 新規プロジェクトの初期段階で、nonnullとnullableの使用に関するルールを文書化し、明確にします。
  2. 既存のコードにnonnull属性を後から適用する場合は、コードレビューを通じて徐々に適用範囲を拡大します。
  3. チーム内で定期的なレビューセッションを設け、nonnullの使用法について議論し、継続的に学習と改善を促進します。

このように、nonnull属性を適切にカスタマイズし、チームで共有することで、Objective-Cプロジェクトの信頼性を向上させることができます。

まとめ

Objective-Cの開発において、nonnull属性は安全で信頼性の高いコードを書くために不可欠です。

この記事では、nonnull属性の基本から応用まで、その使用方法を10個のサンプルコードを通じて紹介しました。

各サンプルコードは、実際のプログラミングシーンを想定し、初心者から経験豊富な開発者までが役立つ内容となっています。

Objective-Cにおけるnonnullの正しい理解と適切な使い方を身につけることで、nullに関連するバグのリスクを減らし、より堅牢なアプリケーション開発が可能になります。

nonnull属性の適用は、コードの堅牢性を高める一方で、過度に厳格なルールが生産性を妨げないようにバランスを取ることが重要です。

プロジェクト固有のルールを設定し、チームメンバー間での合意形成を行うことで、一貫性のあるコーディングスタンダードを確立しましょう。

これにより、Objective-Cのnonnull理解度を5倍に高め、より良いプログラムを作成することができるでしょう。