Расширение класса Swift с помощью категории Objective C

Im в ситуации, когда мне нужно использовать Objective C для расширения класса Swift. Я сделал что-то следующее:

В "SomeClass.swift":

class SomeClass: NSObject {
}

В "SomeClass + Extension.h":

#import "Project-Swift.h"
@interface SomeClass (Extension) 
-(void)someMethod();
@end

Это сработало хорошо. И если я попытаюсь использовать расширение SomeClass в моем коде Objective C, это нормально.

Проблема в том, что если я хочу использовать someMethod() в другом классе Swift, мне нужно будет поместить файл SomeClass+Extension.h в мой файл ObjC-BridgingHeader.h.

Но выполнение этого приведет к зависимости circuclar, потому что SomeClass+Extension.h также импортирует Project-Swift.h.

Есть ли у кого хороший способ обойти это?

Обратите внимание, что просто переадресация объявления класса в заголовке категории не будет работать, поскольку категории не могут использовать форвардные объявления для собственной реализации так:

@class SomeClass без импорта Project-Swift.h даст ошибку компиляции.

Ответы

Ответ 1

Плохой

Я тоже борюсь с этой проблемой. к сожалению, documentation довольно явно заявляет, что этот шаблон не разрешен:

Чтобы избежать циклических ссылок, не импортируйте код Swift в Objective-C заголовок (.h). Вместо этого вы можете переслать объявление Swift класс или протокол для ссылки на него в интерфейсе Objective-C.

Передовые декларации классов и протоколов Swift могут использоваться только как типы для объявлений методов и свойств.

также на всей связанной странице вы заметите, что он продолжает упоминать, чтобы импортировать сгенерированный заголовок специально в файл .m:

Чтобы импортировать код Swift в Objective-C из той же цели

Импортировать код Swift из этой цели в любой файл Objective-C.m внутри этой цели

Хороший

одним из решений, которое может работать для вас, является создание быстрого расширения, которое переопределяет каждый метод, который вам нужен в этой категории. это хрупкое и уродливое, но, возможно, самое чистое решение.

/**
 Add category methods from objc here (since circular references prohibit the ObjC extension file)
 */
extension SomeClass {
    @nonobjc func someMethod() {
        self.performSelector(Selector("someMethod"))
    }
}
  • добавление @noobjc к фронту позволяет та же сигнатура метода, которая будет использоваться без переопределения реализации ObjC
  • теперь import "SomeClass+Extension.h" от моста заголовок может быть удален

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

Ответ 2

Как одно простое решение, вы можете переместить расширение на ваш код Swift. Тогда у вас не будет проблем с зависимостью.

Ответ 3

В Руководстве по совместимости мы не можем напрямую обращаться к объектам Objc подкласса/категоризации/расширения для класса .swift [SomeClass].

Но как поворот, мы можем это сделать:

Для переменных мы можем это сделать:

extension Class {
    private struct AssociatedKeys {
        static var DescriptiveName = "sh_DescriptiveName"
    }

    var descriptiveName: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
        }

        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.DescriptiveName,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }
}

Для методов мы можем использовать метод_swizzling, который не рекомендуется.