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

Вот цитата из другого сообщения:

Я работаю в проекте iOS, который включает статическую библиотеку, созданную другой компанией. В библиотеку включена старая версия AFNeworking, и у меня нет исходных файлов.

Теперь мне нужно использовать более новую (и менее подверженную ошибкам) ​​версию afneworking, но я не могу включить тот же класс дважды в проект (конечно), потому что все "дублированные символы"

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

Единственный способ избежать "дублирования символов" и "класса X" реализован как в Y, так и в Z. Один из двух будет использоваться ", который приходит мне на ум, это добавить некоторый префикс к исходным классам инфраструктуры, но это правильное решение?

ОБНОВЛЕНИЕ 1:

Я попытался применить решение Джона, но без радости. Я создал упрощенный проект (вот ссылка на репо) с двумя классами FrameworkClass, которые присутствуют только в рамках целевой среды, и SharedClass, которые присутствует как в инфраструктуре, так и в приложениях, поэтому, возможно, вы увидите, что я делаю что-то неправильно. После запуска приложения я все еще получаю: objc[96426]: Class SharedClass is implemented in both .../TestFramework.framework/TestFramework and .../SymbolsVisibilityTest.app/SymbolsVisibilityTest. One of the two will be used. Which one is undefined

ОБНОВЛЕНИЕ 2:

Вот мой вывод из nm на основе предоставленного образца проекта-вывода:

0000000000007e14 t -[FrameworkClass doFramework]
0000000000007e68 t -[SharedClass doShared]
                 U _NSLog
                 U _NSStringFromSelector
00000000000081f0 s _OBJC_CLASS_$_FrameworkClass
                 U _OBJC_CLASS_$_NSObject
0000000000008240 s _OBJC_CLASS_$_SharedClass
00000000000081c8 s _OBJC_METACLASS_$_FrameworkClass
                 U _OBJC_METACLASS_$_NSObject
0000000000008218 s _OBJC_METACLASS_$_SharedClass
0000000000007fb0 s _TestFrameworkVersionNumber
0000000000007f70 s _TestFrameworkVersionString
                 U ___CFConstantStringClassReference
                 U __objc_empty_cache
                 U _objc_release
                 U _objc_retainAutoreleasedReturnValue
                 U dyld_stub_binder`

ОБНОВЛЕНИЕ 3:

Мне удалось "скрыть" символы SharedClass, применив решение от @bleater, а мой вывод из nm теперь:

         U _NSLog
         U _NSStringFromSelector
00001114 S _OBJC_CLASS_$_FrameworkClass
         U _OBJC_CLASS_$_NSObject
00001100 S _OBJC_METACLASS_$_FrameworkClass
         U _OBJC_METACLASS_$_NSObject
         U ___CFConstantStringClassReference
         U __objc_empty_cache
         U _objc_release
         U _objc_retainAutoreleasedReturnValue
         U dyld_stub_binder`

Но я все еще получаю предупреждение о двойной реализации в Xcode.

Ответ 1

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

Это позволяет избежать всех проблем, которые вы описали. Затем вы можете включить любую версию любой публичной библиотеки (AFNetworking, SQLite и т.д.), Не опасаясь конфликта в будущем, потому что что-либо, связанное с вашей каркасной или библиотекой, не сможет "увидеть" эти символы.

Чтобы установить скрытую видимость по умолчанию, вы можете войти в настройки проекта и установить для параметра "Символы, скрытые по умолчанию" значение "ДА". Он установлен в НЕТ, если вы не измените его.

enter image description here

Есть как минимум несколько способов пометить символы из вашего открытого интерфейса как "Видимые". Один из них - с помощью файла экспорта, другой - для прохождения и явно отмечать определенные функции как видимые:

#define EXPORT __attribute__((visibility("default")))
EXPORT int MyFunction1();

Определение, очевидно, просто для удобства. Вы определяете EXPORT один раз, а затем просто добавляете EXPORT ко всем своим общедоступным символам.

Здесь вы можете найти официальную документацию на Apple:

Руководство по программированию среды выполнения

Update:

Я взглянул на ваш образец проекта, и похоже, я указал вам в неправильном направлении. Похоже, вы можете только скрывать символы C и С++. Поэтому, если у вас была эта проблема с C lib (например, sqlite), установка видимости по умолчанию для скрытия будет работать. Похоже, что характер выполнения Objective C не позволяет вам сделать символы невидимыми. Вы можете отметить видимость этих символов, но с Objective-C, похоже, это всего лишь способ заставить компоновщика использовать то, что вы должны или не должны использовать из библиотеки (пока все еще оставляете их видимыми).

Итак, если вы переопределите символ Objective-C в другой единице компиляции с тем же именем (возможно, скомпилировав в новой версии популярной библиотеки с открытым исходным кодом), у вас все равно будет конфликт.

Я думаю, что ваше единственное решение на этом этапе - это сделать то, что вы предложили, и префикс символов, которые вы включаете в свою структуру, с уникальным идентификатором. Это не очень изящное решение, но с ограничениями целевой среды выполнения C я считаю, что это, вероятно, лучшее решение.

Ответ 2

Итак, сообщение в блоге Kamil Burczyk было хорошей отправной точкой, спасибо за подсказку Michał Ciuba! Он охватил большинство символов, но он не справился с категориями и кластерами классов. Вы можете видеть, какие методы категорий все еще отображаются без каких-либо изменений, вызывая nm с помощью списка параметров sth, например:

nm MyLibrary | grep \( | grep -v "\[LIBRARYPREFIX" | grep -v \(MyLibrary | grep -v ") prefix_"

Когда дело доходит до категорий, мы имеем 3 группы категорий, и все они требуют определенного, другого подхода:

  • Категории классов, которые были переименованы NamespacedDependencies.h
  • Категории классов, не переименованные в NamespacedDependencies.h
  • Категории кластеров классов, например NSString, NSArray...

Объявление 1.

Все в порядке - имя класса будет префиксом, поэтому категория будет существовать в префиксе sumbol в объектном файле

Объявление 2.

Эта проблема возникает всякий раз, когда внутри зависимостей мы имеем категорию в классе, подобном NSObject. Он будет отображаться без каких-либо изменений в объектном файле, что может вызвать конфликт. Мой подход состоял в том, чтобы внутренне переименовать NSObject в PREFIX_NSObject, это, конечно же, требует от меня также создания и добавления реализации класса PREFIX_NSObject для проекта (пустая реализация, просто подкласс оригинала NSObject)

#import "PREFIX_NSObject.h"
#ifndef NSValueTransformer
#define NSValueTransformer __NS_SYMBOL(NSObject)
#endif

Объявление 3.

Мы не можем применять Ad 2. подход здесь. Фактические объекты, созданные, например, PREFIX_NSArray методы класса по-прежнему имеют тип, который не будет выводиться из моего предполагаемого класса PREFIX_NSArray, поэтому это не имеет смысла, поскольку методы категорий, определенные на PREFIX_NSArray, не будут видны на NSArray производных объектов. Я закончил с помощью методов префикса вручную этих категорий в исходном коде.

Это своего рода сумасшедший рабочий процесс, но, по крайней мере, дает гарантию, что все будет "невидимым" и не вызовет конфликта.

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

nm MyLibrary | grep \( | grep -v "\[LIBRARYPREFIX" | grep -v \(MyLibrary | grep -v ") prefix_"