Objective- C Каркасы и столкновения пространства имен - PullRequest
1 голос
/ 17 февраля 2020

Мы хотим поместить 2 версии нашего приложения iOS в один пакет. Таким образом, наши клиенты могут вернуться к прежней версии после отправки обновления. Я надеялся добиться этого, встроив текущую и предыдущие версии в фреймворки и вызвав соответствующую версию после запроса пользователя.

В качестве теста я создал два Frameworks, LibA и LibB, каждый из которых содержит класс Thing.

Проблема, с которой я сталкиваюсь, - это предупреждение во время выполнения ...

objc[21117]: Class Thing is implemented in both /private/var/containers/Bundle/Application/0E6374C5-52FB-421F-90D6-ADC9A4C22B5D/DualBootTestApp.app/Frameworks/LibA.framework/LibA (0x102b144b0) and /private/var/containers/Bundle/Application/0E6374C5-52FB-421F-90D6-ADC9A4C22B5D/DualBootTestApp.app/Frameworks/LibB.framework/LibB (0x102ba4460). One of the two will be used. Which one is undefined.

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

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

Обновление

Я только что попробовал использовать библиотеку Cocoa Touch Stati c. С библиотекой stati c я не получаю предупреждение о времени выполнения, но всегда вызывается версия класса Thing из LibB, даже когда вызов происходит из LibA.

Я начинаю верить, что Макро-префикс целевых c имен классов может быть единственным решением. Много общего кода делает это мрачной перспективой.

Кто-нибудь знает, как я могу скрыть имена классов, чтобы только у каждого Framework была видимость своих классов?

Есть ли лучший подход к добавить две версии приложения в один пакет? Это вообще возможно? Будут ли проблемы с обзором Appstore?

1 Ответ

1 голос
/ 19 февраля 2020

Если вы уже превратили свои классы в фреймворки, поздравляю, вы уже сделали сложную часть. Имейте в виду, однако, что если большая часть кода вашего приложения находится в платформе, некоторые вещи могут работать не так, как ожидалось. Например, любой код, который в конечном итоге вызывает NSBundle.mainBundle (например, +[UIImage imageNamed:]), вероятно, неверен, если вы также версировали свои ресурсы.

Но давайте предположим, что вы успешно интегрировали версии своего приложения.

Вы не можете ссылаться ни на одну из платформ, если хотите выбрать одну из них во время выполнения. Вместо этого вам нужно использовать dlopen и NSClassFromString или dlsym для достижения точки входа.

Вот пример:

#import <dlfcn.h>
#import "HeaderWithEntryPointMethod.h"

void pickedAppVersion(int version) {
    NSString *frameworkName = [NSString stringWithFormat:@"AppV%d", version];
    NSString *frameworkExecutable = [NSString stringWithFormat:@"%@.framework/%@", frameworkName, frameworkName]; // this should traverse the symlink 
    NSString *frameworkPath = [[[NSBundle mainBundle] privateFrameworksPath] stringByAppendingPathComponent:frameworkExecutable];
    void *frameworkHandle = dlopen(frameworkPath.UTF8String, RTLD_NOW);
    if (frameworkHandle != NULL) {
        Class EntryPointClass = NSClassFromString(@"EntryPoint");
        assert(EntryPointClass != Nil);
        [EntryPointClass entryMethod];
        // App framework should do everything from here
        if (dlclose(frameworkHandle) != 0) {
            NSLog(@"failed to close chosen app framework: %s", dlerror());
        }
    }
    else {
        NSLog(@"failed to open app framework: %@ because: %s", frameworkName, dlerror());
    }
}

Что именно означает EntryPoint и +entryMethod это зависит от вас. Если вам нужна точка входа в функцию C, используйте dlsym вместо NSClassFromString.

Re: Обзор App Store: я думаю, это может поднять бровь или две, но пока вы разрешение пользователю, особенно рецензенту, не должно быть проблемой. dlopen обычно используется для выборочной загрузки платформ во время выполнения, чтобы упростить запуск приложений и загрузить функции, используемые приложением по требованию.

...