読み込み中...

【初心者向け】Objective-Cのグローバル変数7つの作成法

初心者が学ぶObjective-Cのグローバル変数のイメージ Objctive-C
この記事は約21分で読めます。

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

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

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

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

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

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

はじめに

プログラミングは、思考を形にする魔法のような手段です。

特に、Objective-CはAppleのiOSやmacOSアプリケーションの開発において長きにわたり使われてきた言語であり、その基本を学ぶことは非常に価値があります。

この記事では、初心者でも理解できるようにObjective-Cにおけるグローバル変数の作成法について詳しく解説していきます。

グローバル変数はプログラムのあらゆる場所からアクセス可能な変数であり、その便利さとともに、使用する際の注意点も存在します。

この記事を読めば、グローバル変数をうまく使いこなす方法と、それに関連する様々なテクニックが身につくでしょう。

●グローバル変数の基礎知識

グローバル変数とは、プログラム内のどこからでもアクセスできる変数のことです。

アプリケーション内で共通の設定や状態を保持するために使われますが、誤った使い方をするとバグの原因にもなり得るため、正しい理解と使用方法が重要です。

グローバル変数は、データの共有が必要な時に非常に便利ですが、そのスコープ(変数が参照できる範囲)が広いため、プログラムのどの部分でどのように変更されているかを把握することが難しくなる可能性があります。

○グローバル変数とは

プログラムの多くの関数やメソッドからアクセス可能な変数のことで、プログラムの実行開始時に生成され、終了時まで存在します。

通常、グローバル変数はプログラムのトップレベルに宣言され、アプリケーション全体で一貫したデータを保持するのに使用されます。

○グローバル変数の利用シーン

Objective-Cでのグローバル変数は次のような場合に役立ちます。

  1. 設定値や環境変数など、アプリケーション全体で共有されるべき情報の保持
  2. 複数のクラスや関数間でのデータ共有
  3. シングルトンパターンの実装において、インスタンスが一つしか存在しないことを保証するため

●Objective-Cでのグローバル変数の作り方

Objective-Cでグローバル変数を作成するプロセスは、他の多くのプログラミング言語と類似していますが、Objective-C独自の特徴を持っています。

グローバル変数を作るためには、まずグローバル変数がプログラムの全体にわたってアクセス可能であることを理解しなければなりません。

これは、変数がプログラムの任意の場所で宣言され、そのスコープがプログラム全体であることを意味します。

グローバル変数の宣言は通常、プログラムの最上部、すなわち他のどの関数やメソッドよりも先に配置されます。

これにより、プログラム実行時には最初にグローバル変数がメモリ上に確保され、終了時までそのメモリが保持され続けることになります。

○基本的なグローバル変数の宣言と初期化

グローバル変数を宣言する基本的な方法は、変数を外部リンケージとして宣言することです。

これは、変数を.mファイルの外部に宣言し、.hヘッダーファイルで外部から参照できるようにすることを意味します。

次に、初期化を行います。

初期化はプログラムの起動時に一度だけ行われ、その値はプログラムが終了するまで変わりません。

グローバル変数の宣言には、externキーワードを使用します。

これにより、変数が他のファイルからも参照可能であることをコンパイラに通知します。

例えば、GlobalData.hというヘッダーファイルがあり、それに次のように記述することができます。

// GlobalData.h
extern int GlobalCounter; // 他のファイルから参照可能なグローバル変数

そして、実際の値を持つグローバル変数は、一つの.mファイルに宣言する必要があります。

// GlobalData.m
int GlobalCounter = 0; // グローバル変数の初期化

○グローバル変数のスコープとアクセス性

グローバル変数のスコープは、それが宣言された時点からプログラムの終了まで続きますが、そのアクセス性はどのソースファイルからでも可能です。

グローバル変数にアクセスするためには、externキーワードを使って宣言された変数に対して、インクルードディレクティブを用いてヘッダーファイルを参照する必要があります。

例えば、ある関数内でグローバル変数を増加させたい場合、次のようにコードを記述します。

// SomeFile.m
#include "GlobalData.h"

void IncrementGlobalCounter() {
    GlobalCounter++; // グローバル変数をインクリメント
}

この関数が呼び出されるたびに、GlobalCounter変数の値は1ずつ増加します。

このように、グローバル変数はプログラムのどこからでも変更が可能であり、これは非常に強力ですが、同時にプログラムの複雑さを増す原因ともなりえます。

○グローバル変数を扱う時のメモリ管理

Objective-Cでは、グローバル変数に対するメモリ管理も考慮する必要があります。

特に、オブジェクトのグローバル変数を扱う場合、適切な時にオブジェクトをリリースすることが重要です。

Objective-Cのautoreleaseプールは、オブジェクトが適切なタイミングでメモリから解放されるようにする仕組みを提供しますが、グローバル変数に対してはautoreleaseを使用することは推奨されません。

なぜなら、グローバル変数はプログラムが終了するまで続く長いライフサイクルを持っているため、予期しないタイミングでの解放が発生しないように管理する必要があるからです。

グローバル変数に割り当てられたオブジェクトは、通常、プログラムの終了時に明示的に解放するか、またはretainreleaseメソッドを使用して参照カウントを管理します。

これにより、メモリリークを防ぐとともに、オブジェクトが必要なくなった時に安全に解放することができます。

●グローバル変数の詳細な使い方

グローバル変数はObjective-Cプログラミングにおいて非常に便利なツールですが、適切に使用するにはいくつかのポイントを理解しておく必要があります。

ここでは、実際にグローバル変数を使う方法とそのサンプルコードを提供し、さらにその実行結果についても詳しく説明します。

○サンプルコード1:アプリケーション全体で使用する設定値

Objective-Cでグローバル変数を使ってアプリケーション全体の設定値を管理する一例を見てみましょう。

下記のコードスニペットは、グローバル変数を使用してユーザーの設定を保存し、アプリケーションの異なる部分からこれらの設定にアクセスする方法を表しています。

// AppSettings.h
extern NSString *const AppThemeColor;
extern CGFloat const AppFontSize;

// AppSettings.m
#import "AppSettings.h"

NSString *const AppThemeColor = @"Blue";
CGFloat const AppFontSize = 14.0;

// ViewController.m
#import "AppSettings.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // グローバル変数を使用して設定を適用する
    self.view.backgroundColor = [UIColor colorWithName:AppThemeColor];
    self.textView.font = [UIFont systemFontOfSize:AppFontSize];
}

@end

このサンプルコードでは、AppSettings.h ファイルで外部から参照できるようにグローバル変数を宣言し、AppSettings.m で具体的な値を設定しています。

ViewController.m では、これらのグローバル変数を用いてビューの背景色とテキストビューのフォントサイズを設定しています。

このように、グローバル変数を使用することで、コードの再利用性を高め、一貫性のある設定管理が可能になります。

○サンプルコード2:複数クラス間でのデータ共有

複数のクラスが同じデータにアクセスする必要がある場合、グローバル変数は非常に有効です。

次に、複数のビューコントローラ間でユーザーのスコアを共有する例を見てみましょう。

// ScoreManager.h
extern NSUInteger UserScore;

// ScoreManager.m
#import "ScoreManager.h"

NSUInteger UserScore = 0;

// GameViewController.m
#import "ScoreManager.h"

@implementation GameViewController

- (void)increaseScore {
    UserScore += 10;
    NSLog(@"ユーザースコア更新: %lu", (unsigned long)UserScore);
}

@end

// ScoreViewController.m
#import "ScoreManager.h"

@implementation ScoreViewController

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.scoreLabel.text = [NSString stringWithFormat:@"スコア: %lu", (unsigned long)UserScore];
}

@end

ここでは、ScoreManager.h でグローバル変数 UserScore を宣言し、ScoreManager.m で初期化しています。

GameViewController クラス内の increaseScore メソッドでは、このスコアを増加させ、ScoreViewControllerviewWillAppear メソッドでスコアを表示しています。

これにより、アプリケーションの異なる場所でスコアを簡単に共有し、更新することができます。

○サンプルコード3:定数としてのグローバル変数

グローバル変数は変更可能なデータを格納するために使われることが多いですが、変更されない定数として使用することもあります。

Objective-Cでは、const キーワードを使用して定数を宣言することができます。

下記のコードは、アプリケーションで使用されるいくつかの定数をグローバル変数として宣言する方法を表しています。

// Constants.h
extern NSString *const BaseAPIURL;
extern NSString *const AnalyticsAPIKey;

// Constants.m
#import "Constants.h"

NSString *const BaseAPIURL = @"https://api.example.com";
NSString *const AnalyticsAPIKey = @"your_api_key_here";

// SomeService.m
#import "Constants.h"

@implementation SomeService

- (void)fetchData {
    NSURL *url = [NSURL URLWithString:BaseAPIURL];
    // URLを使用してデータを取得する
}

@end

このコードでは、APIのベースURLと分析APIキーをグローバル変数として定義しており、アプリケーションのどこからでもこれらの値にアクセスできるようになっています。

定数として宣言されたグローバル変数は、コンパイル時にその値が固定され、実行時に変更することはできません。

●グローバル変数の応用例

グローバル変数は多様なプログラミングシナリオで活用されます。

Objective-Cを含む多くの言語で、グローバル変数はデータ共有、状態管理、設定値の保持など、様々な目的で使われることがあります。

ここでは、具体的なグローバル変数の応用例を4つ紹介し、それぞれに対するサンプルコードとその実行結果を紹介します。

○サンプルコード4:状態管理の共有

Objective-Cでの状態管理におけるグローバル変数の使用例を紹介します。

アプリケーションが複数の状態を持ち、それらが異なるコンポーネントによって共有される必要がある場合、グローバル変数が役立ちます。

// StateManager.h
extern BOOL isUserLoggedIn;

// StateManager.m
#import "StateManager.h"

BOOL isUserLoggedIn = NO;

// LoginController.m
#import "StateManager.h"

@implementation LoginController

- (void)login {
    // ユーザーがログインした場合、グローバル変数を更新
    isUserLoggedIn = YES;
}

@end

// UserController.m
#import "StateManager.h"

@implementation UserController

- (void)userAction {
    if (isUserLoggedIn) {
        // ユーザーがログインしている場合のみ行う処理
    }
}

@end

このコードでは、ユーザーのログイン状態をグローバル変数isUserLoggedInを使用して管理しています。

ログインコントローラでユーザーがログインすると、この変数がYESに設定され、ユーザーコントローラではログインしているかどうかに応じて適切なアクションを実行します。

○サンプルコード5:マルチスレッド環境での使用

マルチスレッドプログラミングにおいてもグローバル変数は有用です。

例えば、同時に実行される複数のスレッドがあるデータを共有する必要がある場合、次のようにグローバル変数を利用できます。

// ThreadManager.h
extern NSMutableArray *sharedResource;

// ThreadManager.m
#import "ThreadManager.h"

NSMutableArray *sharedResource;

@implementation ThreadManager

+ (void)initialize {
    if (self == [ThreadManager class]) {
        sharedResource = [NSMutableArray array];
    }
}

// 別スレッドで実行されるメソッド
- (void)modifySharedResource {
    @synchronized(sharedResource) {
        // リソースへの変更を行う
    }
}

@end

ここで重要な点は、共有リソースへのアクセスをスレッドセーフにするために@synchronizedブロックを使用していることです。

これにより、同時にリソースにアクセスしようとする複数のスレッド間で競合が起こるのを防ぎます。

○サンプルコード6:外部設定ファイルからの読み込み

外部の設定ファイルから情報を読み込んでグローバル変数に格納することで、アプリケーションの設定を一元管理することができます。

// ConfigManager.h
extern NSDictionary *appConfig;

// ConfigManager.m
#import "ConfigManager.h"

NSDictionary *appConfig;

@implementation ConfigManager

+ (void)loadAppConfig {
    NSString *configPath = [[NSBundle mainBundle] pathForResource:@"AppConfig" ofType:@"plist"];
    appConfig = [NSDictionary dictionaryWithContentsOfFile:configPath];
}

@end

ConfigManagerクラスは、アプリケーションの起動時に設定ファイルAppConfig.plistから設定を読み込み、appConfigグローバル変数に格納します。

これにより、アプリケーション内のどこからでも設定情報にアクセスできます。

○サンプルコード7:イベント駆動型プログラミングにおける使用

イベント駆動型プログラミングでは、イベントが発生するとグローバル変数を介して他のコンポーネントに通知することがよくあります。

下記のコードは、イベントリスナーがグローバル変数を監視し、変更があった場合に行動を起こすシンプルな例です。

// EventManager.h
extern NSString *currentEvent;

// EventManager.m
#import "EventManager.h"

NSString *currentEvent;

// EventListener.m
#import "EventManager.h"

@implementation EventListener

- (void)checkForEvents {
    if ([currentEvent isEqualToString:@"UserLoggedIn"]) {
        // ユーザーがログインしたことを検知した場合の処理
    }
}

@end

これらの例は、グローバル変数がObjective-Cでどのように使われるかを表すためのものです。

実際の実行結果は、アプリケーションのロジックやユーザーの操作に応じて異なりますが、これらのコードスニペットはグローバル変数の様々な応用を理解するのに役立つはずです。

●グローバル変数の注意点と対処法

グローバル変数の使い方には多くのメリットがある一方で、適切な管理と使用が求められます。

グローバル変数を使う際の主なリスクやそれに対する対処法を解説します。

○グローバル変数を使用する際のリスク

□名前の衝突

グローバル変数はプログラムのあらゆる場所からアクセスできるため、異なるライブラリやモジュールで同じ名前の変数が定義されていると予期せぬ衝突が起きる可能性があります。

□スレッドの安全性

マルチスレッドプログラムでは、複数のスレッドが同時にグローバル変数を読み書きすることで、データの整合性が損なわれるリスクがあります。

□データの整合性

グローバル変数が適切に更新されない場合、アプリケーションの異なる部分でデータの不一致が発生することがあります。

□テストと保守の難しさ

グローバル変数が多用されると、プログラムが複雑になり、テストと保守が困難になることがあります。

○グローバル変数の競合を避ける方法

グローバル変数の名前が競合する問題を避けるためには、ユニークな名前空間を作成するか、クラス変数を使用する方法があります。

例えば、プロジェクト固有のプレフィックスを変数名に付けることで、名前の衝突を減少させることができます。

// MyAppStateManager.h
extern BOOL MYAppStateIsUserLoggedIn;

// MyAppStateManager.m
BOOL MYAppStateIsUserLoggedIn = NO;

この方法では、MYAppというプレフィックスを使用してグローバル変数を定義しており、他のライブラリやモジュールとの名前の衝突を避けています。

○デバッグと保守のヒント

デバッグと保守を容易にするために、グローバル変数の使用を最小限に抑え、必要な場所でのみアクセスするように制限することが重要です。

また、変数の変更箇所を追跡しやすくするために、変数の変更を行う関数やメソッドにコメントを残すと良いでしょう。

// GlobalCounterの値を変更する関数
void IncreaseGlobalCounter() {
    // GlobalCounterの値を増加させる前に、現在の状況をログに記録
    NSLog(@"GlobalCounter was %d before increment", GlobalCounter);
    GlobalCounter++;
    NSLog(@"GlobalCounter is now %d after increment", GlobalCounter);
}

グローバル変数の使用は、それが適切なケースにおいて有用ですが、上記のリスクを理解し、適切な管理と保守の実践を心がけることが不可欠です。

●グローバル変数のカスタマイズ方法

グローバル変数のカスタマイズは、柔軟性とプログラムの整理を向上させるために重要です。

カスタマイズは、グローバル変数の安全な使用、コードの可読性の向上、およびメンテナンスの簡素化に役立ちます。

ここでは、Objective-Cにおけるグローバル変数のカスタマイズ方法をいくつか紹介します。

○グローバル変数のカプセル化

カプセル化は、グローバル変数への直接アクセスを制限し、代わりに関数やメソッドを通じてアクセスを管理するプラクティスです。

これにより、グローバル変数の使用をコントロールしやすくなります。

// GlobalConfig.h
void SetGlobalConfig(NSString *configKey, id value);
id GetGlobalConfig(NSString *configKey);

// GlobalConfig.m
#import "GlobalConfig.h"

static NSMutableDictionary *globalConfigDictionary;

void InitializeGlobalConfig() {
    globalConfigDictionary = [[NSMutableDictionary alloc] init];
}

void SetGlobalConfig(NSString *configKey, id value) {
    [globalConfigDictionary setValue:value forKey:configKey];
}

id GetGlobalConfig(NSString *configKey) {
    return [globalConfigDictionary valueForKey:configKey];
}

このコードスニペットでは、SetGlobalConfigGetGlobalConfig 関数を使用してグローバル変数へのアクセスを管理しています。

これにより、変数の値の設定と取得が関数を通じてのみ行われ、直接の変更や不適切な使用を防ぐことができます。

○グローバル変数の動的初期化

グローバル変数はプログラムの実行開始時に初期化されますが、場合によっては動的に初期化する必要があります。

これは、実行時の条件に基づいて変数を設定する場合に特に有用です。

// 动的に初期化されるグローバル変数の例
NSString *GetDocumentsDirectoryPath() {
    static NSString *documentsDirectoryPath = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    });
    return documentsDirectoryPath;
}

上の例では、dispatch_onceを用いてドキュメントディレクトリのパスを一度だけ計算し、その後は変数を再利用しています。

これにより、計算コストの高い操作を何度も繰り返さないようにしています。

○条件付きコンパイルを用いたカスタマイズ

条件付きコンパイルは、コンパイル時に特定のコードが含まれるかどうかを制御する方法です。

これは、開発と本番環境のコードを分けたり、デバッグ情報の有無を切り替えたりするのに使われます。

// DebugLogging.h
void DebugLog(NSString *message);

// DebugLogging.m
#import "DebugLogging.h"

void DebugLog(NSString *message) {
    #ifdef DEBUG
    NSLog(@"Debug: %@", message);
    #endif
}

このコードでは、DEBUGシンボルが定義されている場合のみ、デバッグメッセージをログに出力します。

リリースビルドでこのシンボルを定義しないことによって、デバッグコードを本番コードから除外することができます。

まとめ

この記事では、Objective-Cにおけるグローバル変数の作成法から利用シーン、注意点、カスタマイズ方法までを網羅的に説明しました。

グローバル変数は、アプリケーション全体でデータを共有する際に非常に便利ですが、その使用には慎重な検討が必要です。

名前の衝突、スレッド安全性、データの整合性、テストと保守の難しさなど、いくつかのリスクが伴います。

これらのリスクに対処するためには、変数のカプセル化、動的初期化、条件付きコンパイルなどのテクニックを使用することが推奨されます。

カプセル化により、グローバル変数へのアクセスを関数やメソッドを介して行い、変数の使用を明示的に制御できます。

動的初期化を利用することで、実行時に変数の初期値を設定し、プログラムの柔軟性を高めることができます。ま

た、条件付きコンパイルを使用することで、開発環境と本番環境の間でコードを切り替え、不要なデバッグ情報が本番環境に含まれないようにすることが可能です。

Objective-Cのプログラミングにおいてグローバル変数を扱うことは、避けがたい場合がありますが、この記事で紹介したベストプラクティスを適用することで、より安全で管理しやすいコードの作成が可能となります。

プログラミング初心者も、ここで学んだ知識を基にして、Objective-Cでの効果的なグローバル変数の使用法をマスターできるでしょう。