ios загружает класс objective c против класса swift при отражении с использованием objective c

#ios #swift #objective-c #reflection

Вопрос:

Я пытаюсь загрузить класс, используя отражение в objective c, и получаю разные результаты для класса swift и класса objective c

Я вижу, что, используя NSClassFromString, для класса objective c мне нужно ввести только имя класса, а для класса swift мне также нужно ввести идентификатор пакета в качестве префикса

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

Примеры ниже

напр.

цель c класс — А

быстрый класс — В

идентификатор пакета проекта является тестовым

 Class  test1 = NSClassFromString(@"A"); // works

Class  test2 = NSClassFromString(@"Test.A"); // doesn't work

Class  test3 = NSClassFromString(@"B"); // doesn't work

Class  test4 = NSClassFromString(@"Test.B"); // works
 

Я попытался сделать то же самое в swift и получил ту же проблему

 var  test1 = NSClassFromString("A") as? A.Type // works

var  test2 = NSClassFromString("Test.A") as? A.Type // doesn't work

var  test3 = NSClassFromString("B") as? B.Type // doesn't work

var  test4 = NSClassFromString("Test.B") as? B.Type // works
 

Парень

Комментарии:

1. Так в чем же проблема использования A классов ObjC и Test.B swift?

2. Мне было интересно, есть ли API, в котором одна и та же строка может работать как для классов objective c, так и для swift

3. вы уже нашли один API, проблема в том, что swift организован в модулях, что является концепцией, которая не существует в objc. Но в соответствии с соглашениями об именовании имена классов можно найти и дифференцировать, если они являются objc или swift .. например, с именем . amp; модуля впереди. Поиск имен классов в пространстве других языков не рекомендуется, но, очевидно, возможен. Поскольку у обоих есть свой особый механизм перехода на другой язык, вы сталкиваетесь с проблемами. Это означает, что вы можете явным образом предоставить класс swift @objc(desiredname) и найти его в objc с этим именем после того, как соединение будет выполнено должным образом.

Ответ №1:

Классы Swift используют пространство имен и сортируются по модулям

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

 // NSObject ExposedSwiftClassExtension.h
// make use of @objc exposed swift classes
// with mainBundle name
//
#import <Foundation/Foundation.h>

@interface NSObject (ExposedSwiftClassExtension)

/** @name classWithNameAndTryDifferentIfFail:
    gets Class with NSString, if fails try to find in MainBundle
    or with last component, otherwise nil.
    @discussion should find Class with Name in first attempt, if 
    no success tests for "." in objcString and tries without
    preleading module name or within MainBundle as last resort.
    If all three ways fail returns nil.
    @param objcString a NSString as assumed ClassName
    @return Class or nil if not found.
 */
  (Class _Nullable)classWithNameAndTryDifferentIfFail:(NSString* _Nonnull)objcString;

@end
 
 // NSObject ExposedSwiftClassExtension.m
// ExposedSwiftClassExtension,
// make use of @objc exposed swift classes
// but they use namespace and module name
// so this extension makes it convenient
// if module name is bundle name
//
#import "NSObject ExposedSwiftClassExtension.h"

@implementation NSObject (ExposedSwiftClassExtension)
 (Class)classWithNameAndTryDifferentIfFail:(NSString*)objcString {
    Class desiredClass = NSClassFromString(objcString);
    if (desiredClass!=nil) return desiredClass;
    NSString *classname = nil;
    if ([objcString containsString:@"."]) {
        // contains "." means you assume module but try without here.
        classname = [objcString componentsSeparatedByString:@"."].lastObject;
    } else {
        // last resort, try with BundleName
        NSString *bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleName"];
        if (bundleName) {
            classname = [bundleName stringByAppendingFormat:@".%@",objcString];
        }
    }
    // if classname is still nil here should return nil anyway.
    return classname ? NSClassFromString(classname) : nil;
}
@end
 

Затем используйте Class test2 = [NSObject classWithNameAndTryDifferentIfFail:@"Test.A"];

PS: для начинающих objc.. Как вы можете видеть в коде, конфликты имен классов выполняются очень легко, всегда приятно, когда имена классов уникальны, и «ExposedSwiftClassExtension» может быть изменен на что-то другое, если непонятно, почему я выбираю это имя.. Это было задумано как напоминание о том, что незащищенные классы swift нельзя найти в Objective-C. Расширение могло бы быть написано на Swift, но затруднило бы использование в ObjC.

Комментарии:

1. спасибо, прочитав об этом, я понял, что apple реализовала классы objective c и swift по-разному, поэтому swift создается из другой строки, чем. цель c. Это был мой вопрос

2. да. ObjC использует строки, что делает его таким гибким, поэтому NSClassFromString он существует и полезен. Swift в этом смысле отличается, поэтому имена функций должны быть явно доступны ObjC, но предлагают аналогичные функции для выполнения аналогичной работы. Соединение ObjC 2 Swift-это наполовину автоматизированный процесс с помощью соглашений об именах, соединение Swift 2 ObjC выполняется явно с помощью функций маркировки и/или классов с @objc впереди. Проще отметить swift @objc и использовать одно объявление в objc, которое можно подключить к swift, чтобы использовать его как в том, так и в другом случае, или объявить его дважды..