Swift не вызывает метод init класса на CAGradientLayer - PullRequest
3 голосов
/ 21 февраля 2020

У меня есть подкласс Objective- C класса CAGradientLayer, который переопределяет инициализатор слоя + (CAGradientLayer *). Он правильно включен в мой Bridging-Header

#import "ONCGradientLayer.h"

@implementation ONCGradientLayer

+ (ONCGradientLayer *)gradientLayer {
    return [ONCGradientLayer layer];
}

+ (ONCGradientLayer *)layer {
    ONCGradientLayer * layer = [super layer];
    layer.colors = @[(id)[[UIColor colorWithWhite:0.0 alpha:1] CGColor], (id)[[UIColor colorWithWhite:0.1 alpha:1] CGColor]];
    layer.startPoint = CGPointMake(0, 0);
    layer.endPoint = CGPointMake(0, 1);
    return layer;
}

Это все работает, как и ожидалось от Objective- C, вызывая либо [ONCGradientLayer layer], либо [ONCGradientLayer gradientLayer]

Когда я пытаюсь вызовите его из swift, я получил предупреждение компилятора о том, что метод был переименован в init, поэтому я должен вызвать ONCGradientLayer() Это, однако, не работает, и переопределенный инициализатор никогда не вызывается.

Я могу обойти это, добавив инициализатор с другим именем:

+ (ONCGradientLayer *)swiftInit {
    return [ONCGradientLayer layer];
}

Я могу успешно вызвать это в swift, используя ONCGradientLayer.swiftInit()

Что происходит, что мешает мне позвонить ONCGradientLayer() и получить переопределенный инициализатор?

ОБНОВЛЕНИЕ: я могу заставить ONCGradientLayer() работать, реорганизовав его для использования метода init, хотя мне все еще любопытно, почему другой подход не работает.

- (instancetype)init {
    if (self = [super init]) {
        self.colors = @[(id)[DEFAULT_BACKGROUND_COLOR CGColor], (id)[[UIColor colorWithWhite:.1 alpha:1] CGColor]];
        self.startPoint = CGPointMake(0, 0);
        self.endPoint = CGPointMake(0, 1);
    }
    return self;
}

1 Ответ

4 голосов
/ 22 февраля 2020

Это все о компоненте Swift, называемом Clang Importer. Он отвечает за перевод имен методов Objective- C в имена методов Swift (и наоборот).

Clang Importer предполагает, что если у класса Objective C есть метод класса с тем же именем, что и у класса, за исключением префикса и нижнего регистра, возможно расширенных на with, , это фабрика классов method.

Предполагается также, что метод фабрики классов эффективен так же, как и инициализатор, имя которого построено таким же образом, а полностью скрывает соответствующий фабричный метод, поскольку AR C Между этими двумя значениями нет существенной разницы.

ПРИМЕР: NSString имеет фабричный метод stringWithFormat: и инициализатор initWithFormat:.

Well , string - это то же самое, что NSString без префикса и в нижнем регистре; поэтому Clang Importer предполагает, что stringWithFormat: является заводским методом. Более того, он построен так же, как initWithFormat:, поэтому Clang Importer предполагает, что они фактически одинаковы, и скрывает первый.

Поэтому вы должны сказать String(format:), что означает String.init(format:), в Swift. [Clang Importer также лишает with, но это уже другая история.] В Swift нет String.withFormat(_:) (или аналогичного), соответствующего Objective- C stringWithFormat:.

That точно такая же ситуация с вашим ONCGradientLayer и gradientLayer. Ваш метод класса следует соглашению об именовании методов фабрики классов. Следовательно, благодаря Clang Importer, если есть init - и есть - метод фабрики классов скрыт. Поэтому вместо этого вы должны позвонить init, сказав ONCGradientLayer() на языке Swift.

Но вы не определили init для возврата модифицированного ONCGradientLayer, поэтому в результате получается простой слой без ваших изменений.

Если вам это не нравится, у вас есть два варианта. И вы уже обнаружили, что они есть!

  • Измените имя фабрики классов, например makeGradientLayer. (Это именно то, что вы сделали, указав имя swiftInit.)

  • Или определите init, чтобы вернуть ONCGradientLayer, измененный так же, как вы это делали в layer , (Это то, что вы тоже сделали.)

...