objective-c 中的覆盖和自定义初始化

Overwriting and custom inits in objective-c

我有一个 class 继承了 MPMoviePlayerViewController。我正在尝试重写 init 方法,这样我就不必为每个其他 -initWithSomething 函数重复代码。对于自定义 initWithSomething 方法,这将起作用。但我不知道如何让它适用于继承的 iniWithSomething 方法

-(instancetype)init
{
    if(self = [super init]){
        // This is code I don't want to repeat in initWithSomething methods
        [self startWithHiddenControls];

        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
        [audioSession setActive:YES error:nil];

        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:UIApplicationDidEnterBackgroundNotification
                                                      object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(UIApplicationDidBecomeActive)
                                                     name:UIApplicationDidBecomeActiveNotification
                                                   object:nil];
    }
    return self;
}

-(instancetype)initWithContentURL:(NSURL *)contentURL
{

    // Not overwriting doesn't use my overwriten init so i must do it.
    // But how ?!?
    // why [super init] makes inifinite loop? (I know the norm is [super <same method>])
    if(self = [self init]){} // this will not work. Inifinite loop I believe.

    return self;
}

//This method works fine
- (instancetype)initWithSettings:(NSDictionary *)settings
{
    // [self init] works here but HOW?
    if(self = [self init]){
        //my custom stuff
    }
}

在写这个post的过程中,我发现[super init]调用了-(instancetype)initWithContentURL:(NSURL *)contentURL,这就是死循环问题。为什么指定初始化器 init 调用辅助初始化器 initWithURL?不应该反过来吗?请解释。

这是否意味着我应该将我不想重复的代码放在 initWithURL 方法而不是 init 方法中。

编辑:这就是我所做的。我在 initWithURL 方法中输入了代码。现在默认的 init 和我的自定义 init 都会 运行 它。

只需创建一个私有方法并将您的公共代码放在那里,例如

- (void)commonInitialization {
    [self startWithHiddenControls];
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
    [audioSession setActive:YES error:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(UIApplicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
}

然后在你的初始化中使用:

- (instancetype)initWithContentURL:(NSURL *)contentURL {
    self = [super initWithContentURL:contentURL];
    if (self) {
        [self commonInitialization];
        // everything else
    }
    return self;
}

为什么称 init 为指定初始化器?我相信,事实并非如此。参考一下the docs:

The designated initializer plays an important role for a class. It ensures that inherited instance variables are initialized by invoking the designated initializer of the superclass. It is typically the init... method that has the most parameters and that does most of the initialization work, and it is the initializer that secondary initializers of the class invoke with messages to self.

所以它似乎不满足这两个特性:它的参数数量最少,而且显然它用 self 调用了另一个初始化程序 (initWithContentURL)。所以,我相信,如果你找到了真正的指定初始化器,一切都会按预期工作。

根据MPMoviePlayerViewController class reference,指定初始化器是initWithContentURL。所以你可以简单地覆盖它而不是 init.