Каков наилучший способ решить коллизию пространства имен Objective C?

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

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

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

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

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


Обновить:

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

7.10.2008 13:27:41
У вас есть хороший вопрос (что делать, если вам нужны две фреймворки с конфликтом имен), но он скрыт в тексте. Пересмотрите, чтобы сделать его более понятным, и вы избежите упрощенных ответов, подобных тому, который вы имеете сейчас.
benzado 7.10.2008 19:53:42
Это моя самая большая проблема с текущим дизайном языка Objective-C. Посмотрите на ответы ниже; те, которые фактически решают этот вопрос (выгрузка NSBundle, использование DO и т. д.), являются отвратительными взломами, которые просто не должны быть необходимы для чего-то столь же тривиального, как избегание конфликта пространства имен.
erikprice 1.03.2009 17:30:40
@erikprice: Аминь. Я изучаю obj-c и попал в эту самую проблему. Пришел сюда в поисках простого решения .... хромой.
Dave Mateer 5.02.2010 19:36:12
Напомним, что технически и C, и Objective-C обеспечивают поддержку нескольких пространств имен - не совсем то, что ищет OP. См objectivistc.tumblr.com/post/3340816080/...
user557219 20.02.2011 09:41:58
Хм, я этого не знал. Какого-то ужасного дизайнерского решения нет?
Nico 20.05.2012 02:44:23
13 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

РЕДАКТИРОВАТЬ: Принципиальное различие между средами выполнения C и Objective-C, насколько я понимаю, при загрузке библиотек функции в этих библиотеках содержат указатели на любые символы, на которые они ссылаются, тогда как в Objective-C они содержат строковые представления названия этих символов. Таким образом, в вашем примере вы можете использовать dlsym, чтобы получить адрес символа в памяти и прикрепить его к другому символу. Другой код в библиотеке все еще работает, потому что вы не меняете адрес исходного символа. Objective-C использует таблицу соответствия для сопоставления имен классов с адресами, и это сопоставление 1-1, поэтому у вас не может быть двух классов с одинаковым именем. Таким образом, чтобы загрузить оба класса, один из них должен изменить свое имя. Тем не менее, когда другие классы должны получить доступ к одному из классов с этим именем,

47
8.10.2008 05:25:08
Я полагаю, что выгрузка пакета не была поддержана до 10.5 или позже.
Quinn Taylor 16.06.2009 17:45:02

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

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

0
7.10.2008 14:14:04
Как префикс файлов помогает, если объекты, определенные в файлах, вызывают проблему? Конечно, я могу манипулировать h-файлами, но тогда компоновщик больше не найдет объекты, когда ему нравится структура: - /
Mecki 7.10.2008 15:04:18
Я считаю, что это было бы лучше - практикой, чем решением первоначальной проблемы.
Ali 23.08.2012 08:15:18
Это вообще не решает вопрос
Madbreaks 2.01.2013 18:39:03

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

@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

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

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

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

93
24.09.2011 16:06:15
Upvote для compatibility_alias, я не знал об этом! Спасибо. Но это не решает мою проблему. Я не контролирую изменение каких-либо префиксов той или иной фреймворка, который я использую, у меня они только в двоичном виде и они конфликтуют. Что мне с этим делать?
Mecki 7.10.2008 22:46:49
В каком файле (.h или .m) должна идти строка @compatibility_alias?
Alex Basson 23.04.2010 14:19:48
@Alex_Basson @compatibility_alias, вероятно, должен идти в заголовке, чтобы он был виден везде, где видна декларация @interface.
Barry Wark 23.04.2010 16:06:03
Интересно, можете ли вы использовать @compatibility_alias с именами протоколов, или определениями typedef, или что-нибудь еще по этому вопросу?
Ali 23.08.2012 08:12:30
Хорошие мысли здесь. Может ли метод swizzling быть использован для предотвращения NSClassFromString (ClassName) от возврата nil для псевдонимов классов? Вероятно, было бы трудно найти все методы, которые принимают имя класса.
Dickey Singh 4.11.2013 06:12:27

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

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

4
7.12.2008 18:35:53

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

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

3
27.02.2009 07:49:06
Изменение библиотеки на диске не подлежит сомнению, потому что лицензия не позволяет этого. Изменение символов в памяти было бы возможно, но я не вижу способа, как это можно сделать (загрузить библиотеку в память, изменить ее, а затем передать ее динамическому компоновщику ... для этого не найдено никакого метода).
Mecki 27.02.2009 15:54:25
ОК - время решить, какая из двух библиотек менее важна, и найти замену. И объясните тому, за кого вы платите, но что вы отказываетесь, что причина, по которой вы меняете, заключается в этом столкновении, и они могут сохранить ваш бизнес, решив вашу проблему.
Jonathan Leffler 27.02.2009 21:11:42

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

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

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

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

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

8
28.02.2009 00:00:52

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

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

0
5.03.2009 21:20:00

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

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

1
4.03.2009 03:54:42
Но разве вы все равно не столкнетесь с коллизиями, если попытаетесь включить оба заголовка-обертки в один и тот же файл (поскольку каждый из них должен будет сам включать заголовки своей соответствующей платформы)?
Wilco 1.06.2009 15:39:49

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

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

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

12
16.06.2009 17:43:34

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

cc foo.o -ldog bar.o -lcat

Если foo.oи bar.oкак ссылка символ , ratто libdogрассосется foo.o«s ratи libcatрассосется bar.o» S rat.

0
20.05.2012 03:27:45

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

-1
18.01.2013 00:54:49

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

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

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

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

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

0
31.05.2013 16:01:52

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

@compatibility_alias NewAliasClass OriginalClass;

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

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

2
26.12.2013 19:04:26