iOS
objectivec

Objective-Cのクラス初期化

More than 3 years have passed since last update.

Objective-Cのクラス初期化

  1. + (void)initializeを利用
  2. + (void)loadを利用
  3. __attribute__((constructor))を利用

*こちらの説明は動的ロードなどによる挙動は考慮してないです

1.+ (void)initializeを利用

呼ばれるタイミング

  • クラスが最初に利用される直前

注意点

  • サブクラスで+ (void)initializeを実装しないとスーパークラスの+ (void)initializeが呼ばれるので複数呼ばれる前提で実装する
@interface Parent : NSObject
@end
@implementation Parent
// このinitializeはParentクラスとChildクラスが最初に使われる時に
// 呼ばれるので2回呼ばれることになる。
// なのでクラスを確認するかdispatch_onceなどを利用して初期化する
+ (void)initialize
{
    if (self == [Parent self]) {
        NSLog(@"initialize %@", [self class]);
    }

//    dispatch_onceを利用
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
//        NSLog(@"initialize %@", [self class]);
//    });
}
@end

@interface Child : Parent
@end
@implementation Child
@end
  • + (void)initializeは各クラスで一度しか呼ばれないのでクラス本体でのみ実装してカテゴリでは実装しない
@implementation Parent
+ (void)initialize
{
    NSLog(@"initialize %@", [self class]);
}
@end

@implementation Parent (category)
// NG !!
+ (void)initialize
{
    NSLog(@"initialize %@", [self class]);
}
@end
  • クラスがプログラム内で利用されなければ呼ばれない

2. + (void)loadを利用

呼ばれるタイミング

  • クラスがメモリにロードされた直後、main関数が呼ばれる前

注意点

  • 他のクラスはまだロードされてない可能性があるので他のクラスは利用しない
@implementation Parent
+ (void)load
{
    // NG !!
    OtherClass *otherClass = [OtherClass new];
}
@end
  • クラス本体とカテゴリで複数実装すると全て呼ばれる
@implementation Parent
+ (void)load
{
    NSLog(@"load");
}
@end

@implementation Parent (category)
+ (void)load
{
    NSLog(@"load by category");
}
@end

@implementation Parent (category2)
+ (void)load
{
    NSLog(@"load by category2");
}
@end

3. __attribute__((constructor))を利用

呼ばれるタイミング

  • main関数が呼ばれる直前、全クラスがロード済み
@interface Parent : NSObject
@end

__attribute__((constructor))
static void initialize_class()
{
    NSLog(@"initialize_class");
}

@implementation Parent
+ (void)initialize
{
    if (self == [Parent self]) {
        NSLog(@"initialize %@", [self class]);
    }
}

+ (void)load
{
    NSLog(@"load");
}
@end

int main(int argc, char * argv[]) {
    @autoreleasepool {

        Parent *p = [Parent new];
        NSLog(@"%@", p);
    }
}

// ログは以下のように表示される
// load
// initialize_class
// initialize Parent
// <Parent: 0xXXXXXXXXXXX>

注意点

  • これは正確に言うとObjective-Cの機能ではなくGCC提供の関数属性機能です。

GCC - Declaring Attributes of Functions

  • 複数の__attribute__((constructor))は優先度を指定して呼ばれる順番を指定可能
__attribute__((constructor(2)))
static void initialize_class2()
{
    NSLog(@"initialize_class 2");
}


__attribute__((constructor(3)))
static void initialize_class3()
{
    NSLog(@"initialize_class 3");
}


__attribute__((constructor(1)))
static void initialize_class1()
{
    NSLog(@"initialize_class 1");
}


// ログは以下のように表示される
// initialize_class 1
// initialize_class 2
// initialize_class 3