#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, чтобы использовать его как в том, так и в другом случае, или объявить его дважды..