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

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


최신 정보:

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



답변

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

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

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

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


답변

고유 한 접두사로 클래스 접두사를 사용하는 것이 기본적으로 유일한 옵션이지만 이것을 덜 번거롭고 추악하게 만드는 몇 가지 방법이 있습니다. 옵션에 대한 긴 토론이 여기에 있습니다 . 내가 가장 좋아하는 것은 @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)에서 작동해야합니다.


답변

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

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

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


답변

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

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

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

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

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


답변

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

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


답변

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

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


답변

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

@compatibility_alias NewAliasClass OriginalClass;

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

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