Каков наилучший способ избежать дублирования символов в проекте, который будет использовать мою инфраструктуру 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_"