Каков наилучший способ решения конфликта объектов Objective-C?

Objective-C не имеет пространств имен; это похоже на C, все в одном глобальном пространстве имен. Обычной практикой является префикс classов с инициалами, например, если вы работаете в IBM, вы можете префикс их с помощью «IBM»; если вы работаете в Microsoft, вы можете использовать «MS»; и так далее. Иногда инициалы ссылаются на проект, например, Adium префикс classов с «AI» (поскольку за ним нет компании, что вы могли бы взять инициалы). Apple префикс classов с NS и говорит, что этот префикс зарезервирован только для Apple.

Пока все хорошо. Но добавление от 2 до 4 букв к имени classа впереди – очень ограниченное пространство имен. Например, MS или AI могут иметь совершенно разные значения (например, AI может быть искусственным интеллектом), а другой разработчик может решить использовать их и создать одинаково названный class. Bang , конфликт пространства имен.

Хорошо, если это столкновение между одним из ваших собственных classов и одной из внешних фреймворков, которые вы используете, вы можете легко изменить название вашего classа, неважно. Но что, если вы используете две внешние среды, обе структуры, к которым у вас нет источника, и которые вы не можете изменить? Ваше приложение связывается с обоими из них, и вы получаете конфликты имен. Как бы вы решили их решить? Каков наилучший способ обойти их таким образом, что вы все равно можете использовать оба classа?

В C вы можете обойти их, не связывая напрямую с библиотекой, вместо этого вы загружаете библиотеку во время выполнения, используя dlopen (), затем найдите символ, который вы ищете, используя dlsym () и назначьте его глобальному символу (который вы можно назвать любым способом), а затем получить доступ к нему через этот глобальный символ. Например, если у вас есть конфликт, потому что у некоторой библиотеки C есть функция с именем open (), вы можете определить переменную с именем myOpen и указать ее на функцию open () библиотеки, поэтому, когда вы хотите использовать open () , вы просто используете open (), а когда хотите использовать другой, вы получаете доступ к нему через идентификатор myOpen.

Возможно ли что-то подобное в Objective-C, а если нет, есть ли еще какое-то другое умное, сложное решение, которое вы можете использовать конфликты пространства имен? Есть идеи?


Обновить:

Просто для того, чтобы прояснить это: ответы, которые предлагают, как избежать конфликтов пространства имен заранее или как создать лучшее пространство имен, безусловно, приветствуются; однако я не буду принимать их в качестве ответа, так как они не решают мою проблему. У меня две библиотеки, и имена их classов сталкиваются. Я не могу их изменить; У меня нет источника ни одного. Столкновение уже существует, и подсказки о том, как его можно было избежать заранее, больше не помогут. Я могу направить их разработчикам этих фреймворков и надеюсь, что они в будущем будут выбирать лучшее пространство имен, но пока я ищу решение для работы с фреймворками прямо сейчас в рамках одного приложения. Любые решения, чтобы сделать это возможным?

13 Solutions collect form web for “Каков наилучший способ решения конфликта объектов Objective-C?”

Если вам не нужно одновременно использовать classы из обеих фреймворков, и вы ориентируетесь на платформы, которые поддерживают разгрузку NSBundle (OS X 10.4 или новее, поддержка GNUStep), и производительность действительно не проблема для вас, я считаю, что вы можете загружать одну структуру каждый раз, когда вам нужно использовать class, а затем выгружать ее и загружать другую, когда вам нужно использовать другую фреймворк.

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

Вам не нужно было бы копировать или переименовывать class, если бы был способ скопировать данные, на которые указывает IMP. Вы можете создать новый class, а затем скопировать поверх ivars, методов, свойств и категорий. Гораздо больше работы, но это возможно. Тем не менее, у вас все еще будет проблема с другими classами в структуре, ссылающимися на неправильный class.

EDIT: фундаментальное различие между временем выполнения C и Objective-C – это, как я понимаю, при загрузке библиотек, функции в этих библиотеках содержат указатели на любые символы, которые они ссылаются, тогда как в Objective-C они содержат строковые представления имена символов. Таким образом, в вашем примере вы можете использовать dlsym, чтобы получить адрес символа в памяти и прикрепить его к другому символу. Другой код в библиотеке по-прежнему работает, потому что вы не меняете адрес исходного символа. Objective-C использует таблицу поиска для сопоставления имен classов с адресами, и это сопоставление 1-1, поэтому вы не можете иметь два classа с одинаковым именем. Таким образом, чтобы загрузить оба classа, одно из них должно изменить свое имя. Однако, когда другим classам необходимо получить доступ к одному из classов с этим именем, они запросят таблицу поиска для своего адреса, и таблица поиска никогда не вернет адрес переименованного classа с учетом имени исходного classа.

Префикс ваших classов с уникальным префиксом в принципе является единственным вариантом, но есть несколько способов сделать это менее обременительным и уродливым. Здесь обсуждается множество вариантов. Моя любимая директива компилятора @compatibility_alias Objective-C (описанная здесь ). Вы можете использовать @compatibility_alias для «переименования» classа, позволяющего вам называть ваш class с помощью FQDN или какого-то такого префикса:

 @interface COM_WHATEVER_ClassName : NSObject @end @compatibility_alias ClassName COM_WHATEVER_ClassName // now ClassName is an alias for COM_WHATEVER_ClassName @implementation ClassName //OK //blah @end ClassName *myClass; //OK 

В рамках полной страtagsи вы можете префикс всех ваших classов с помощью уникального префикса, такого как полное доменное имя, а затем создать заголовок со всеми параметрами @compatibility_alias (я бы предположил, что вы можете автоматически генерировать упомянутый заголовок).

Недостатком такого префикса является то, что вы должны ввести истинное имя classа (например, COM_WHATEVER_ClassName выше) во всем, что требует имя classа из строки, кроме компилятора. Примечательно, что @compatibility_alias – это директива компилятора, а не функция времени выполнения, поэтому NSClassFromString(ClassName) завершит сбой (return nil ) – вам нужно будет использовать NSClassFromString(COM_WHATERVER_ClassName) . Вы можете использовать ibtool через фазу сборки, чтобы изменить имена classов в интерфейсе Builder nib / xib, чтобы вам не приходилось писать полный COM_WHATEVER _… в Interface Builder.

Заключительное предостережение: поскольку это директива компилятора (и неясная), она может быть не переносимой между компиляторами. В частности, я не знаю, работает ли он с интерфейсом Clang из проекта LLVM, хотя он должен работать с LLVM-GCC (LLVM с использованием интерфейса GCC).

Несколько человек уже поделились каким-то хитрым и умным кодом, который может помочь решить проблему. Некоторые из предложений могут работать, но все они менее идеальны, и некоторые из них совершенно противны для реализации. (Иногда уродливые хаки неизбежны, но я стараюсь избегать их, когда могу.) С практической точки зрения, вот мои предложения.

  1. В любом случае, сообщите разработчикам обеих структур конфликта и дайте понять, что их неспособность избежать и / или иметь дело с ним вызывает серьезные проблемы с бизнесом, что может привести к потере доходов бизнеса, если они не будут решены. Подчеркните, что при разрешении существующих конфликтов на уровне каждого classа это менее навязчивое решение, полностью изменяющее их префикс (или использование одного, если они не в настоящее время, и стыд за них!) – лучший способ гарантировать, что они не будут снова повторите эту проблему.
  2. Если конфликты именования ограничены достаточно небольшим набором classов, посмотрите, можете ли вы обойти только эти classы, особенно если один из конфликтующих classов не используется вашим кодом прямо или косвенно. Если это так, посмотрите, будет ли поставщик предоставлять пользовательскую версию фреймворка, которая не включает конфликтующие classы. Если нет, будьте откровенны в том, что их негибкость снижает рентабельность инвестиций, используя их frameworks. Не чувствуйте себя плохо в том, чтобы быть назойливым в разумных пределах – клиент всегда прав. 😉
  3. Если одна структура более «непригодна», вы можете подумать о ее замене другой картой (или комбинацией кода), либо сторонней, либо доморощенной. (Последний является нежелательным наихудшим случаем, поскольку он, безусловно, будет нести дополнительные бизнес-затраты, как для разработки, так и для обслуживания.) Если вы это сделаете, сообщите об этом поставщику этой структуры, почему вы решили не использовать их frameworks.
  4. Если обе структуры считаются одинаково необходимыми для вашей заявки, изучите способы отделить использование одного из них к одному или нескольким отдельным процессам, возможно, обмениваясь через DO, как предложил Луи Гербарг. В зависимости от степени общения это может быть не так плохо, как вы могли бы ожидать. Несколько программ (в том числе QuickTime, я считаю) используют этот подход для обеспечения более гранулярной безопасности, предоставляемой с помощью профилей песочницы Seatbelt в Leopard , так что только конкретное подмножество вашего кода разрешено выполнять критические или чувствительные операции. Производительность будет компромиссом, но может быть вашим единственным вариантом

Я предполагаю, что лицензионные сборы, сроки и длительность могут предотвратить мгновенное действие по любому из этих пунктов. Надеюсь, вы сможете как можно скорее разрешить конфликт. Удачи!

Это грубо, но вы можете использовать распределенные объекты , чтобы сохранить один из classов только в адрес подчиненных программ и RPC. Это будет беспорядочно, если вы передаете тонну вещей туда и обратно (и может быть невозможным, если оба classа напрямую манипулируют представлениями и т. Д.).

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

Если вы действительно в отчаянии, то что вы можете сделать:

  1. Не ссылаться непосредственно на одну из библиотек
  2. Внедрите альтернативную версию подпрограмм выполнения objc, которые меняют имя во время загрузки (проверка проекта objc4 , что именно вам нужно сделать, зависит от ряда вопросов, которые я задал выше, но это должно быть возможно независимо от того, что ответы ).
  3. Используйте что-то вроде mach_override, чтобы внедрить новую реализацию
  4. Загрузите новую библиотеку с помощью обычных методов, она пройдет через исправленную процедуру компоновщика и изменит ее имя classа

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

Рассматривали ли вы использование функций времени выполнения (/usr/include/objc/runtime.h), чтобы клонировать один из конфликтующих classов в не сталкивающийся друг с другом class, а затем загружать встречную структуру classов? (это потребует, чтобы сталкивающиеся фреймворки загружались в разное время, чтобы работать.)

Вы можете проверять classы ivars, методы (с именами и адресами реализации) и именами с исполняемой средой, а также динамически создавать свои собственные, чтобы иметь одинаковый макет ivar, имена методов / адреса реализации и только отличаться по имени (чтобы избежать столкновение)

Отчаянные ситуации требуют отчаянных мер. Вы рассматривали возможность взлома объектного кода (или файла библиотеки) одной из библиотек, изменение встречного символа на альтернативное имя – той же длины, но с другой орфографией (но рекомендация с той же длиной имени)? Неразумно.

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

Похоже, проблема заключается в том, что вы не можете ссылаться на файлы заголовков обеих систем в одной и той же системе перевода (исходный файл). Если вы создаете обертки объектива-c вокруг библиотек (что делает их более пригодными для использования в процессе) и только # включает заголовки для каждой библиотеки в реализации classов-оболочек, что эффективно делит коллизии имен.

У меня недостаточно опыта с этим в объективном-c (только на начальном этапе), но я считаю, что это то, что я сделал бы на C.

@compatibility_alias сможет разрешать конфликты пространства имен classов, например

 @compatibility_alias NewAliasClass OriginalClass; 

Однако это не приведет к разрешению коллизий переписей, typedefs или протоколов . Кроме того, он не очень хорошо работает с @class forward decls исходного classа. Поскольку большинство фреймворков будут поставляться с такими неclassическими элементами, как typedef, вы, вероятно, не сможете исправить проблему с именами с помощью только совместимости.

Я рассмотрел аналогичную проблему для вас , но у меня был доступ к источнику и строился фреймворк. Лучшим решением, которое я нашел для этого, было использование @compatibility_alias условно с #defines для поддержки перечислений / typedefs / protocols / etc. Вы можете сделать это условно в блоке компиляции для рассматриваемого заголовка, чтобы свести к минимуму риск расширения контента в других конфликтующих рамках.

Префикс файлов – это самое простое решение, о котором я знаю. Cocoadev имеет страницу пространства имен, которая является попыткой сообщества избегать конфликтов пространства имен. Не стесняйтесь добавлять свои собственные в этот список, я считаю, для чего он нужен.

http://www.cocoadev.com/index.pl?ChooseYourOwnPrefix

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

За более техническое решение, если бы я был на вашем месте, это было бы моим выбором.

Если столкновение происходит только на уровне статической связи, вы можете выбрать, какая библиотека используется для разрешения символов:

 cc foo.o -ldog bar.o -lcat 

Если foo.o и bar.o оба ссылаются на символ rat тогда libdog разрешит libdog foo.o , а libcat решит bar.o

Просто мысль … не проверена или не доказана и не может быть способом маркировки, но вы подумали о написании адаптера для classа, который вы используете из более простых фреймворков .. или, по крайней мере, их интерфейсов?

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

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

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

Опять же, никоим образом не доказано, но хотелось добавить перспективу. Надеюсь, поможет 🙂

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

  • Объективные переменные уровня статического classа
  • Каким должен быть мой объектив-C-сингл?
  • Как добавить subview с собственным UIViewController в Objective-C?
  • Interesting Posts

    Автоматизация экспорта календаря из Outlook в Google

    Есть ли способ подавить предупреждение Excel 2007 «в другом формате, чем указано в расширении файла»?

    Как добавить пользовательскую комбинацию клавиш для вложенного элемента меню?

    Преобразование Parallels VM в виртуальную виртуальную машину VM?

    Шаблон Django, как найти значение словаря с переменной

    Bash цитирует unescaped при подстановке команд

    Можно ли удалить папку `C: \ ProgramData \ Microsoft`, не нарушая программы?

    Как получить имя пакета из любого места?

    Как форматировать ячейки Excel для отображения продолжительности в годах и в месяцах?

    Firefox: это соединение не доверено + За корпоративным брандмауэром

    Что эквивалентно команде Linux updateb для Mac?

    USB-накопитель распознается в Linux, а не в окнах, mkfs, в каком типе?

    Автоматически отключать Windows при выключении

    Поставщик Microsoft.ACE.OLEDB.12.0 не зарегистрирован на локальной машине

    Группировка поля в одной сводной таблице группирует одно и то же поле в другой таблице

    Давайте будем гением компьютера.