Objective-C 네임 스페이스 충돌을 해결하는 가장 좋은 방법은 무엇입니까?


174

Objective-C에는 네임 스페이스가 없습니다. C와 매우 흡사합니다. 모든 것이 하나의 전역 네임 스페이스 내에 있습니다. 일반적인 관행은 클래스 앞에 이니셜을 붙이는 것입니다. 예를 들어 IBM에서 작업하는 경우 접두어에 "IBM"을 붙일 수 있습니다. Microsoft에서 일하는 경우 "MS"를 사용할 수 있습니다. 등등. 때때로 이니셜은 프로젝트를 참조합니다 (예 : Adium 접두사 클래스에는 "AI"가 붙습니다 (이니셜을 가져올 수있는 회사가 없기 때문에). Apple은 NS로 클래스 접두사를 지정하며이 접두사는 Apple 전용으로 예약되어 있습니다.

여태까지는 그러나 클래스 이름 앞에 2 ~ 4자를 추가하는 것은 매우 제한적인 네임 스페이스입니다. 예를 들어 MS 또는 AI는 완전히 다른 의미를 가질 수 있으며 (예 : AI는 인공 지능 일 수 있음) 일부 다른 개발자는이를 사용하여 동일한 이름의 클래스를 만들 수 있습니다. Bang , 네임 스페이스 충돌.

자, 이것이 자신의 클래스 중 하나와 사용중인 외부 프레임 워크 중 하나와 충돌하는 경우 클래스 이름을 쉽게 변경할 수 있습니다. 그러나 소스가없고 변경할 수없는 두 가지 외부 프레임 워크를 사용한다면 어떨까요? 응용 프로그램이 둘 다 연결되며 이름 충돌이 발생합니다. 이 문제를 어떻게 해결하겠습니까? 두 클래스를 계속 사용할 수있는 방법으로 문제를 해결하는 가장 좋은 방법은 무엇입니까?

C에서는 라이브러리에 직접 연결하지 않고 이러한 문제를 해결할 수 있습니다. 대신 런타임에 dlopen ()을 사용하여 라이브러리를로드 한 다음 dlsym ()을 사용하여 찾고있는 기호를 찾아 전역 기호에 할당하십시오 ( 원하는 방식으로 이름을 지정한 다음이 전역 기호를 통해 액세스 할 수 있습니다. 예를 들어 일부 C 라이브러리에 open ()이라는 함수가 있기 때문에 충돌이 발생하면 myOpen이라는 변수를 정의하고 라이브러리의 open () 함수를 가리 키도록 할 수 있습니다. 따라서 시스템 open ()을 사용하려는 경우 open ()을 사용하고 다른 것을 사용하려면 myOpen 식별자를 통해 액세스하십시오.

Objective-C에서 비슷한 것이 가능합니까? 그렇지 않으면 네임 스페이스 충돌을 해결할 수있는 영리하고 까다로운 솔루션이 있습니까? 어떤 아이디어?


최신 정보:

명확히하기 위해 : 네임 스페이스 충돌을 미리 피하는 방법이나 더 나은 네임 스페이스를 만드는 방법을 제안하는 답변은 확실히 환영합니다. 그러나 그들은 내 문제를 해결하지 못하기 때문에 대답 으로 받아들이지 않을 것입니다. 두 개의 라이브러리가 있고 클래스 이름이 충돌합니다. 변경할 수 없습니다. 어느 쪽이든 소스가 없습니다. 충돌은 이미 존재하며 사전에 피할 수있는 방법에 대한 팁은 더 이상 도움이되지 않습니다. 이러한 프레임 워크 개발자에게 전달할 수 있으며 앞으로 더 나은 네임 스페이스를 선택하기를 희망하지만 당분간은 단일 응용 프로그램 내에서 프레임 워크를 사용할 수있는 솔루션을 찾고 있습니다. 이를 가능하게하는 솔루션이 있습니까?


7
좋은 질문이 있습니다 (이름 충돌이있는 두 개의 프레임 워크가 필요한 경우해야 할 일)하지만 텍스트에 묻혀 있습니다. 더 명확하게 수정하면 현재와 같은 단순한 답변을 피할 수 있습니다.
benzado

4
이것이 현재 Objective-C 언어 디자인과 가장 큰 관련이 있습니다. 아래 답변을보십시오. 실제로 문제를 해결하는 사람들 (NSBundle 언로드, DO 사용 등)은 네임 스페이스 충돌을 피하는 것처럼 사소한 일이 필요하지 않은 끔찍한 해킹입니다.
erikprice 2016 년

@erikprice : 아멘. 나는 obj-c를 배우고 있으며이 문제에 부딪쳤다. 간단한 해결책을 찾기 위해 여기 왔습니다 .... 절름발이.
Dave Mateer

1
레코드의 경우 기술적으로 C와 Objective-C는 OP가 찾고있는 것이 아니라 여러 네임 스페이스를 지원합니다. 참조 objectivistc.tumblr.com/post/3340816080/...

흠, 나는 몰랐다. 끔찍한 디자인 결정의 종류?
Nico

답변:


47

두 프레임 워크의 클래스를 동시에 사용할 필요가 없으며 NSBundle 언로드 (OS X 10.4 이상, GNUStep 지원 없음)를 지원하는 플랫폼을 대상으로하고 있으며 성능이 실제로 문제가되지 않는 경우 클래스를 사용해야 할 때마다 하나의 프레임 워크를로드 한 다음 다른 프레임 워크를 사용해야 할 때 언로드하고 다른 프레임 워크를로드 할 수 있습니다.

필자의 초기 아이디어는 NSBundle을 사용하여 프레임 워크 중 하나를로드 한 다음 해당 프레임 워크 내부의 클래스를 복사하거나 이름을 바꾸고 다른 프레임 워크를로드하는 것입니다. 이것에는 두 가지 문제가 있습니다. 먼저 클래스의 이름을 바꾸거나 클래스를 복사하도록 지정된 데이터를 복사하는 함수를 찾을 수 없었으며 이름이 바뀐 클래스를 참조하는 첫 번째 프레임 워크의 다른 클래스는 이제 다른 프레임 워크의 클래스를 참조합니다.

IMP가 가리키는 데이터를 복사하는 방법이 있다면 클래스를 복사하거나 이름을 바꿀 필요가 없습니다. 새 클래스를 만든 다음 ivar, 메서드, 속성 및 범주를 복사 할 수 있습니다. 훨씬 더 많은 작업이 가능하지만 가능합니다. 그러나 틀린 클래스를 참조하는 프레임 워크의 다른 클래스에는 여전히 문제가 있습니다.

편집 : C와 Objective-C 런타임의 근본적인 차이점은 라이브러리를로드 할 때 라이브러리의 함수에는 참조하는 모든 심볼에 대한 포인터가 포함되지만 Objective-C에서는 문자열 표현이 포함됩니다. thsoe 기호의 이름. 따라서 예제에서 dlsym을 사용하여 메모리에서 심볼의 주소를 가져 와서 다른 심볼에 연결할 수 있습니다. 원래 심볼의 주소를 변경하지 않기 때문에 라이브러리의 다른 코드는 여전히 작동합니다. Objective-C는 조회 테이블을 사용하여 클래스 이름을 주소에 맵핑하며 1-1 맵핑이므로 동일한 이름을 가진 두 개의 클래스를 가질 수 없습니다. 따라서 두 클래스를 모두로드하려면 클래스 중 하나의 이름이 변경되어야합니다. 그러나 다른 클래스가 해당 이름의 클래스 중 하나에 액세스해야하는 경우,


5
번들 언로드 는 10.5 이상까지 지원되지 않았다고 생각합니다 .
Quinn Taylor

93

고유 한 접두사로 클래스 접두사를 사용하는 것이 기본적으로 유일한 옵션이지만 이것을 덜 번거롭고 추악하게 만드는 몇 가지 방법이 있습니다. 옵션에 대한 긴 토론이 여기에 있습니다 . 내가 가장 좋아하는 것은 @compatibility_aliasObjective-C 컴파일러 지시문입니다 ( 여기에 설명되어 있음 ). @compatibility_aliasFQDN 또는 이와 같은 접두사를 사용하여 클래스 이름을 지정할 수 있도록 클래스를 "이름 바꾸기"하는 데 사용할 수 있습니다 .

@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

완전한 전략의 일환으로 모든 클래스 앞에 FQDN과 같은 고유 한 접두사를 붙인 다음 모든 헤더를 만들 수 있습니다 (이 헤더를 @compatibility_alias자동 생성 할 수 있다고 생각합니다).

이와 같은 접두사의 단점은 COM_WHATEVER_ClassName컴파일러 이외의 문자열에서 클래스 이름이 필요한 모든 항목에 실제 클래스 이름 (예 : 위) 을 입력 해야한다는 것입니다. 특히, @compatibility_alias런타임 함수가 아닌 컴파일러 지시문이므로 NSClassFromString(ClassName)실패 (return nil)합니다-를 사용해야 NSClassFromString(COM_WHATERVER_ClassName)합니다. 당신은 사용할 수 있습니다 ibtool페스 Builder에서 클래스 이름을 수정 빌드 단계를 통해 펜촉 / XIB 그래서 당신은 인터페이스 빌더에서 전체 COM_WHATEVER을 _... 작성하지 않는다.

최종 경고 : 이것은 컴파일러 지시문 (그리고 그에 대한 모호한 지시문)이기 때문에 컴파일러에서 이식성이 없을 수 있습니다. 특히 LLVM 프로젝트의 Clang 프론트 엔드에서 작동하는지는 모르겠지만 LLVM-GCC (GCC 프론트 엔드를 사용하는 LLVM)에서 작동해야합니다.


4
compatibility_alias에 대한 찬성, 나는 그것에 대해 몰랐습니다! 감사. 그러나 그것은 내 문제를 해결하지 못합니다. 나는 내가 사용하고있는 프레임 워크의 접두사를 변경하는 제어 기능이 없으며 바이너리 형식으로 만 충돌합니다. 그것에 대해 어떻게해야합니까?
Mecki

@compatibility_alias 줄은 어느 파일 (.h 또는 .m)에 가야합니까?
Alex Basson

1
@Alex_Basson @compatibility_alias는 아마도 @interface 선언이 보이는 곳 어디에서나 보이도록 헤더에 들어가야합니다.
Barry Wark

프로토콜 이름, typedef 정의 또는 그 문제에 @compatibility_alias를 사용할 수 있는지 궁금합니다.
Ali

여기에 좋은 생각이 있습니다. NSClassFromString (ClassName)이 앨리어싱 된 클래스에 대해 nil을 반환하지 못하도록 메서드 스위 즐링을 사용할 수 있습니까? 클래스 이름을 가진 모든 메소드를 찾기가 어려울 것입니다.
Dickey Singh

12

여러 사람들이 이미 문제를 해결하는 데 도움이되는 까다 롭고 영리한 코드를 공유했습니다. 제안 중 일부는 효과가있을 수 있지만 모두 이상적이지는 않으며 일부는 구현하기가 매우 불쾌합니다. (때로는 추악한 해킹은 피할 수 없지만 가능할 때마다 피하는 것이 좋습니다.) 실용적인 관점에서 다음은 제 제안입니다.

  1. 어쨌든, 개발자에게 충돌 의 프레임 워크를 알리고,이를 피하거나 처리하지 못하면 실제 비즈니스 문제가 발생하여 해결되지 않으면 비즈니스 수익이 손실 될 수 있음을 분명히하십시오. 기존의 갈등을 학급별로 해결하는 것은 덜 방해가되는 해결책이며 접두사를 완전히 바꾸는 것 (또는 현재 존재하지 않는 경우 접두사를 사용하는 것이 부끄러운 방법)이 그렇지 않은지 확인하는 가장 좋은 방법이라는 점을 강조하십시오. 같은 문제를 다시보십시오.
  2. 명명 충돌이 상당히 작은 클래스 집합으로 제한되는 경우 특히 충돌하는 클래스 중 하나를 코드에서 직접 또는 간접적으로 사용하지 않는 경우 해당 클래스 만 해결할 수 있는지 확인하십시오. 그렇다면 공급 업체가 충돌하는 클래스를 포함하지 않는 프레임 워크의 사용자 정의 버전을 제공하는지 확인하십시오. 그렇지 않다면 융통성이 없어서 ROI가 프레임 워크를 사용하지 못하게한다는 사실에 대해 솔직하십시오. 고객이 항상 옳다는 이유만으로도 압박감을 느끼지 마십시오. ;-)
  3. 한 프레임 워크가 "필수적"인 경우 다른 프레임 워크 (또는 코드 조합) (타사 또는 홈 브루)로 바꾸는 것을 고려할 수 있습니다. (후자는 바람직하지 않은 최악의 경우입니다. 개발 및 유지 관리를 위해 추가로 비즈니스 비용이 발생하기 때문입니다.) 그렇게하면 해당 프레임 워크를 공급 업체에 정확히 알려 왜 해당 프레임 워크를 사용하지 않기로 결정했는지를 알려주십시오.
  4. 두 프레임 워크가 모두 응용 프로그램에 없어서는 안될 것으로 간주되는 경우 Louis Gerbarg가 제안한 것처럼 DO를 통해 통신하는 방법 중 하나를 하나 이상의 개별 프로세스에 사용하지 않는 방법을 모색하십시오. 의사 소통 정도에 따라 예상보다 나쁘지 않을 수 있습니다. QuickTime을 포함한 여러 프로그램은이 접근법을 사용하여 Leopard의 Seatbelt 샌드 박스 프로파일 을 사용하여 제공되는보다 세분화 된 보안을 제공하므로 코드의 특정 하위 집합 만 중요하거나 민감한 작업을 수행 할 수 있습니다. 성능은 트레이드 오프이지만 유일한 옵션 일 수 있습니다.

라이센스 비용, 기간 및 기간으로 인해이 시점에서 즉각적인 조치를 취하지 못할 수 있습니다. 바라건대 가능한 빨리 충돌을 해결할 수 있기를 바랍니다. 행운을 빕니다!


8

이것은 거칠지 만 하위 프로그램 주소와 RPC에만 클래스 중 하나를 유지하기 위해 분산 객체 를 사용할 수 있습니다 . 많은 물건을 앞뒤로 전달하면 지저분해질 것입니다 (두 클래스가 직접 뷰를 조작하는 경우 불가능할 수도 있습니다).

다른 잠재적 인 해결책이 있지만 많은 상황이 정확한 상황에 달려 있습니다. 특히 최신 또는 레거시 런타임을 사용하고 있습니까, 32 비트 또는 64 비트의 팻 또는 단일 아키텍처입니까, 어떤 OS 릴리스를 대상으로하는지, 동적으로 링크하거나, 정적으로 링크하거나, 선택 사항이 있습니까? 새 소프트웨어 업데이트를 유지 관리해야 할 수도 있습니다.

당신이 정말로 절망적이라면, 할 수있는 일은 :

  1. 라이브러리 중 하나와 직접 연결되지 않음
  2. 로드시 이름을 변경하는 대체 버전의 objc 런타임 루틴을 구현하십시오 ( objc4 프로젝트를 점검하십시오. 정확히 수행해야하는 것은 위에서 요청한 여러 질문에 따라 다르지만, 응답이 무엇이든 가능해야합니다. ).
  3. mach_override 와 같은 것을 사용 하여 새로운 구현을 주입하십시오.
  4. 일반 메소드를 사용하여 새 라이브러리를로드하면 패치 된 링커 루틴을 통해 className이 변경됩니다.

위의 작업은 상당히 노동 집약적이며 여러 아치와 ​​다른 런타임 버전에 대해 구현 해야하는 경우 매우 불쾌하지만 확실히 작동 할 수 있습니다.


4

런타임 함수 (/usr/include/objc/runtime.h)를 사용하여 충돌하는 클래스 중 하나를 충돌하지 않는 클래스로 복제 한 다음 충돌하는 클래스 프레임 워크를로드하는 것을 고려 했습니까? (이를 위해서는 충돌하는 프레임 워크가 다른 시간에로드되어 작동해야합니다.)

클래스 ivar, 메소드 (이름 및 구현 주소가있는) 및 런타임을 사용하여 이름을 검사하고 동일한 ivar 레이아웃, 메소드 이름 / 구현 주소를 가지도록 고유하고 동적으로 작성하여 이름 만 다릅니다 ( 충돌)


3

절박한 상황은 절박한 조치를 요구합니다. 라이브러리 중 하나의 객체 코드 (또는 라이브러리 파일)를 해킹하여 충돌 기호를 길이는 같지만 철자가 다른 대체 이름으로 바꾸는 것을 고려 했습니까? 본질적으로 불쾌한.

코드가 동일한 이름이지만 구현이 다른 두 함수를 직접 호출하는지 또는 충돌이 간접적인지 여부 (차이가 있는지 여부도 명확하지 않음)는 명확하지 않습니다. 그러나 이름을 바꿀 수있는 외부 기회는 적어도 있습니다. 철자의 차이를 최소화하여 기호가 표에서 정렬 된 순서로 있으면 이름을 바꾸더라도 항목의 순서가 바뀌지 않도록하는 것이 좋습니다. 검색하는 배열이 예상대로 정렬되지 않으면 이진 검색과 같은 것들이 화나게됩니다.


라이센스가 허용하지 않기 때문에 디스크에서 라이브러리를 변경하는 것은 의문의 여지가 없습니다. 메모리에서 기호를 변경하는 것이 가능하지만 어떻게 할 수 있는지 알 수 없습니다 (lib를 메모리에로드하고 수정 한 다음 동적 링커에 전달하는 방법은 없습니다).
Mecki

3
두 라이브러리 중 덜 중요한 라이브러리를 결정하고 대체물을 찾을 시간입니다. 그리고 당신이 지불하는 것에 설명하지만 당신이 변화하는 이유는이 충돌로 인한 것이며 당신의 문제를 해결함으로써 비즈니스를 유지할 수 있다고 포기한다고 설명하십시오.
Jonathan Leffler

2

@compatibility_alias 예를 들어 클래스 네임 스페이스 충돌을 해결할 수 있습니다.

@compatibility_alias NewAliasClass OriginalClass;

그러나 이것은 열거 형, typedef 또는 프로토콜 네임 스페이스 충돌을 해결하지 못합니다 . 또한, @class오리지널 클래스의 포워드 데칼 과 잘 어울리지 않습니다 . 대부분의 프레임 워크에는 typedef와 같은 비 클래스가 포함되어 있으므로 호환성 문제만으로 네임 스페이스 문제를 해결할 수 없을 것입니다.

나는 당신과 비슷한 문제를 보았지만 소스에 액세스 할 수 있었고 프레임 워크를 작성하고있었습니다. 내가 찾은 가장 좋은 해결책 @compatibility_alias은 열거 형 / typedefs / 프로토콜 등을 지원하기 위해 #defines와 조건부 로 사용하는 것입니다. 다른 충돌 프레임 워크에서 항목을 확장 할 위험을 최소화하기 위해 해당 헤더의 컴파일 단위에서이 작업을 조건부로 수행 할 수 있습니다.


1

문제는 동일한 번역 단위 (소스 파일)에서 두 시스템의 헤더 파일을 참조 할 수 없다는 것입니다. 라이브러리 주위에 objective-c 래퍼를 만들고 (프로세스에서 더 유용하게 사용할 수 있도록) 래퍼 클래스 구현시 각 라이브러리의 헤더 만 #include하면 이름 충돌을 효과적으로 분리 할 수 ​​있습니다.

objective-c에서 방금 경험이 충분하지 않지만 (시작하기 만하면) C에서 할 것이라고 믿습니다.


1
그러나 동일한 파일에 두 래퍼 헤더를 모두 포함하려고하면 충돌이 발생하지 않습니까? 각 프레임 워크 헤더를 포함해야하기 때문입니다.
Wilco 2016 년

0

내가 알고있는 가장 간단한 해결책은 파일 접두사입니다. Cocoadev에는 네임 스페이스 충돌을 피하기위한 커뮤니티 노력 인 네임 스페이스 페이지가 있습니다. 이 목록에 자신의 것을 자유롭게 추가하십시오.

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


파일 내에 정의 된 개체가 문제를 일으키는 경우 파일 접두사가 어떻게 도움이됩니까? 나는 확실히 시간의 파일을 조작 할 수 있지만, 프레임 워크에 대해 좋아하는 경우 다음 링커는 더 이상 모두에서 개체를 찾을 수 없습니다 : - /
Mecki

나는 이것이 초기 문제에 대한 해결책보다 오히려 최선의 방법이라고 믿는다.
Ali

이것은 전혀 문제를 해결하지 못한다
Madbreaks

0

충돌이 발생하면 응용 프로그램에서 프레임 워크 중 하나를 리팩터링하는 방법에 대해 열심히 생각하는 것이 좋습니다. 충돌이 발생하면 두 사람이 비슷한 작업을 수행하고 있음을 나타내며 단순히 응용 프로그램을 리팩터링하여 추가 프레임 워크를 사용할 수 있습니다. 이렇게하면 네임 스페이스 문제가 해결 될뿐만 아니라 코드를보다 강력하고 유지 관리하기 쉽고 효율적으로 만들 수 있습니다.

더 기술적 인 해결책으로, 내가 당신의 입장에 있다면 이것이 나의 선택 일 것입니다.


0

충돌이 정적 링크 수준에만있는 경우 심볼을 확인하는 데 사용할 라이브러리를 선택할 수 있습니다.

cc foo.o -ldog bar.o -lcat

경우 foo.obar.o심볼이 모두 참조 rat다음 libdog해결할 수 foo.o의를 rat하고 libcat해결할 수 bar.orat.


0

단지 생각 .. 테스트되지 않았거나 입증되지 않았으며 마크의 방법이 될 수 있지만 더 간단한 프레임 워크 또는 적어도 인터페이스에서 사용하는 클래스에 대한 어댑터 작성을 고려 했습니까?

더 간단한 프레임 워크 (또는 가장 적게 액세스하는 인터페이스) 주위에 랩퍼를 작성하는 경우 랩퍼를 라이브러리로 컴파일 할 수 없습니다. 라이브러리가 미리 컴파일되어 있고 헤더 만 배포하면 기본 프레임 워크를 효과적으로 숨기고 충돌과 함께 두 번째 프레임 워크와 자유롭게 결합 할 수 있습니다.

물론 두 프레임 워크에서 클래스를 동시에 사용해야하는 경우가있을 수 있지만 해당 프레임 워크의 추가 클래스 어댑터에 대한 팩토리를 제공 할 수 있습니다. 그 시점에서 나는 래퍼를 만들기위한 좋은 출발점을 제공 해야하는 두 프레임 워크에서 사용하는 인터페이스를 추출하기 위해 약간의 리팩토링이 필요하다고 생각합니다.

랩핑 된 라이브러리에서 추가 기능이 필요할 때 라이브러리를 빌드하고 변경되면 다시 컴파일 할 수 있습니다.

다시 말하지만, 전혀 입증되지 않았지만 관점을 추가하는 것처럼 느껴졌습니다. 그것이 도움이되기를 바랍니다 :)


-1

기능 이름이 동일한 두 개의 프레임 워크가있는 경우 프레임 워크를 동적으로로드 할 수 있습니다. 우아하지는 않지만 가능합니다. Objective-C 클래스로 수행하는 방법을 모르겠습니다. NSBundle클래스에 특정 클래스를로드하는 메소드가 있다고 생각합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.