Подтвердить что ты не робот

Как вызвать метод класса Objective C в Swift

В моем заголовке Objective-C у меня есть следующее объявление метода класса:

@interface WXMediaMessage : NSObject

    +(WXMediaMessage *) message;

Я попытался вызвать этот метод следующим образом:

var message : WXMediaMessage = WXMediaMessage.message()

Но это не сработает. Я настроил заголовок-заголовок.

ИЗМЕНИТЬ:

Если я вызову метод, как описано, появится сообщение об ошибке 'message()' is unavailable: use object construction 'WXMediaMessage()'.

Если я использую WXmediaMessage(), ошибка исчезает. Тем не менее, вернет тот же результат, что и сообщение [WXMediaMessage], если будет вызвано в Objective C?

MYAPP_bridging_header.h

#ifndef MYAPP_bridging_header_h
#define MYAPP_bridging_header_h

#import "WXApi.h"
#import "WXApiObject.h"

#endif

Фрагмент WXApiObject.h

@interface WXMediaMessage : NSObject

   +(WXMediaMessage *) message;

@end

WXApiObject.m

(It is an external api, so I can't see the content)
4b9b3361

Ответ 1

Я имею в виду: НО ПОЧЕМУ?

Это то, о чем нас спрашивают. Мэтт ответил правильно, что переименование метода устранит проблему, но нам все еще остается вопрос, почему.

Ответ заключается в том, что Swift фактически переводит методы удобства в конструкторы.

Итак, пока нам нужно вызвать метод, подобный этому, в Objective-C:

[WXMediaMessage message];

В Swift мы должны просто называть WXMediaMessage(). В Objective-C это эквивалентно вызову:

[[WXMediaMessage alloc] init];

Важно отметить, что фактический метод factory не вызывается, когда мы используем инициализатор по умолчанию Swift. Это произойдет прямо на init без вызова обертки factory. Но лучшая практика предполагает, что наш метод factory должен быть не более чем [[self alloc] init];. Одно замечательное предложение может быть одноточечным, но если у нас есть метод singleton factory, мы должны следовать шаблону, установленному Apple, и назовите наш метод factory с помощью "общего" префикса:

+ (instancetype)sharedMessage;

И следующий код Swift был бы абсолютно прав:

let message = WXMediaMessage.sharedMessage()

Но если message не является одиночным, тогда он должен быть не более return [[self alloc] init];, что мы получили бы со следующим кодом Swift:

let message = WXMediaMessage()

И вот что нам сообщает сообщение об ошибке:

'message()' is unavailable: use object construction 'WXMediaMessage()'

Сообщение об ошибке сообщает нам использовать инициализатор Swift по умолчанию, а не метод Objective-C factory. И это имеет смысл. Единственная причина, по которой когда-либо хотели методы factory в Objective-C, заключалась в том, что [[MyClass alloc] init] выглядит уродливым. Вся наша инициализация все равно должна выполняться в методе init, однако... не в методе factory, который мы создаем, потому что лучше не смотреть на alloc] init]...


Рассмотрим следующий класс Objective-C:

@interface FooBar : NSObject

+ (instancetype)fooBar;

- (void)test;

@end

@implementation FooBar

- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"initialized");
    }
    return self;
}

+ (instancetype)fooBar {
    NSLog(@"initialized with fooBar");
    return [[self alloc] init];
}

- (void)test {
    NSLog(@"Testing FooBar");
}

@end

Теперь, используя следующий код Swift:

let var1 = FooBar()
var1.test()

Мы получаем следующий вывод:

2014-11-08 10:48:30.980 FooBar[5539:279057] initialized
2014-11-08 10:48:30.981 FooBar[5539:279057] Testing FooBar

Как мы видим, метод fooBar никогда не вызывается. Но действительно, если вы строите свои классы Objective-C правильно с учетом хорошей практики, метод класса, названный как таковой, никогда не должен быть больше:

return [[self alloc] init];

И ваш метод init должен обрабатывать все установленные настройки.

То, что происходит, становится более очевидным, когда мы используем менее простые инициализаторы и методы factory.

Рассмотрим, добавляем ли мы способ инициализации нашего класса числом:

@interface FooBar : NSObject

+ (instancetype)fooBarWithNumber:(int)number;
- (void)test;

@end

@implementation FooBar {
    int _number;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"initialized");
    }
    return self;
}

- (instancetype)initWithNumber:(int)number {
    self = [self init];
    if (self) {
        NSLog(@"init with number: %i", number);
        _number = number;
    }
    return self;
}

+ (instancetype)fooBarWithNumber:(int)number {
    NSLog(@"fooBar with number: %i", number);
    return [[self alloc] initWithNumber:number];
}

- (void)test {
    NSLog(@"Testing FooBar (number: %i)", _number);
}

@end

В этот момент обратите внимание, что наш .h предоставляет метод fooBarWithNumber:, но не метод initWithNumber:.

Теперь вернемся к коду Swift:

let var1 = FooBar(number: 3)
var1.test()

Swift превратил наш метод fooBarWithNumber: в инициализатор: FooBar(number:Int32)

И вот вывод:

2014-11-08 10:57:01.894 FooBar[5603:282864] fooBar with number: 3
2014-11-08 10:57:01.894 FooBar[5603:282864] initialized
2014-11-08 10:57:01.895 FooBar[5603:282864] init with number: 3
2014-11-08 10:57:01.895 FooBar[5603:282864] Testing FooBar (number: 3)

Вызывается метод fooBarWithNumber:, который вызывает initWithNumber:, который вызывает init.


Итак, ответ на вопрос "ПОЧЕМУ" заключается в том, что Swift переводит наши инициализаторы Objective-C и factory в инициализаторы стиля Swift.

Чтобы продолжить дальше, проблема связана не только с самим именем метода. Это комбинация имени метода, имени класса и типа возврата. Здесь мы использовали instancetype как возвращаемый тип. Если мы используем instancetype или наш тип определенного типа, мы сталкиваемся с проблемой. Но, однако, подумайте об этом:

@interface FooBar

+ (NSArray *)fooBar;

@end

И предположим некоторую допустимую реализацию этого метода класса, который соответствует имени класса, но тип возврата НЕ соответствует нашему классу.

Теперь следующий метод является абсолютно корректным кодом Swift:

let fooBarArray = FooBar.fooBar()

Поскольку тип возвращаемого значения не соответствует классу, Swift не может перевести его в инициализатор класса, поэтому наш метод в порядке.

Также стоит отметить, что если мы выберем id как возвращаемый тип нашего метода, как таковой:

+ (id)fooBar;

Свифт позволит нам уйти от него... но он предупредит нас, что наша переменная выведена, чтобы иметь тип "AnyObject!". и рекомендует добавить явный тип, поэтому он рекомендовал бы это:

let var1: FooBar = FooBar.fooBar()

И это позволит нам это сделать... но наилучшая практика почти наверняка рекомендует не использовать id в качестве возвращаемого типа. Apple недавно изменила почти все свои возвращаемые типы id на instancetype.

Ответ 2

Проблема - это имя. Swift не импортирует конструкторы factory. Он обнаруживает их, замечая, что имя совпадает с именем конца класса.

Измените имя метода message на что-то вроде singletonMessage, и все будет хорошо.

Если имя message, вы увидите следующее:

enter image description here

Но если я сменил имя на, скажем, messager, он просто компилируется.

Для аналогии попробуйте следующее:

var thing : HeyHo = HeyHo.ho()

Вы получите то же сообщение об ошибке.