Objective-C : id와 void의 차이점 *


답변:


240

void * "형식화되지 않은 / 알 수없는 내용의 임의의 청크 메모리에 대한 참조"를 의미합니다.

id "알 수없는 클래스의 임의의 Objective-C 객체에 대한 참조"를 의미합니다.

추가적인 의미 차이가 있습니다.

  • GC Only (GC 전용) 또는 GC Supported (GC 지원) 모드에서 컴파일러는 type 참조가 id아닌 쓰기 장벽 을 생성합니다 void *. 구조를 선언 할 때 이것은 중요한 차이가 될 수 있습니다. iVars를 선언 void *_superPrivateDoNotTouch;하면 _superPrivateDoNotTouch실제로 객체 인 경우 객체를 조기에 수확하게됩니다 . 하지마

  • void *유형 의 참조에서 메소드를 호출하려고 시도 하면 컴파일러 경고가 표시됩니다.

  • id유형 에서 메소드를 호출하려고 시도하면 호출 된 메소드 @interface가 컴파일러가 표시 한 선언에서 선언되지 않은 경우에만 경고합니다 .

따라서 객체를 절대로 참조해서는 안됩니다 void *. 마찬가지로, id타입 변수를 사용하여 객체를 참조 하지 않아야 합니다. 가장 구체적인 클래스 형식의 참조를 사용하십시오. 심지어 NSObject *보다 낫다id 컴파일러는 적어도, 그 기준에 대한 메소드 호출의 더 나은 유효성 검사를 제공 할 수 있기 때문이다.

일반적이고 유효한 용도 중 하나 void *는 다른 API를 통해 전달되는 불투명 한 데이터 참조입니다.

sortedArrayUsingFunction: context:방법을 고려하십시오 NSArray:

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;

정렬 함수는 다음과 같이 선언됩니다.

NSInteger mySortFunc(id left, id right, void *context) { ...; }

이 경우 NSArray는 context인수로 전달한 모든 것을 인수로 메소드에 context전달합니다. NSArray에 관한 한 포인터 크기의 데이터가 불투명하고 원하는 목적으로 자유롭게 사용할 수 있습니다.

언어에 클로저 유형 기능이없는 경우, 함수를 사용하여 많은 데이터를 전달할 수있는 유일한 방법입니다. 예; 스레드 안전을 유지하면서 mySortFunc ()가 조건에 따라 대소 문자를 구분하거나 대소 문자를 구분하지 않도록하려면 상황에 따라 대 / 소문자를 구분하는 표시기를 전달합니다.

깨지기 쉽고 오류가 발생하기 쉽지만 유일한 방법입니다.

블록은 이것을 해결합니다-블록은 C의 클로저입니다. Clang에서 사용할 수 있습니다-http: //llvm.org/ 및 Snow Leopard ( http://developer.apple.com/library/ios/documentation/Performance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).


또한, 나는 확신이 있다고 해요 id에 대응하는 가정 -retain하고 -releaseA는 반면, void*호출 수신자에 완전히 불투명하다. -performSelector:withObject:afterDelay:(객체를 유지 하는) 임의의 포인터를 전달할 수 없으며 +[UIView beginAnimations:context:]컨텍스트를 유지 한다고 가정 할 수 없습니다 (애니메이션 대리자는 컨텍스트의 소유권을 유지해야하고 UIKit은 애니메이션 대리자를 유지합니다).
tc.

3
id응답 내용에 대한 가정은 없습니다 . 은 id쉽게에서 고유하지 않는 클래스의 인스턴스를 참조 할 수 있습니다 NSObject. 실제적으로 말하면, 당신의 진술은 실제 행동과 가장 일치합니다. <NSObject>구현 되지 않은 클래스를 Foundation API와 혼합 하여 멀리 갈 수는 없습니다. 확실히 확실합니다!
bbum

2
이제 idClass유형으로 처리되고 보존 가능한 개체 포인터 ARC에서. 따라서 가정은 적어도 ARC 하에서 사실입니다.
eonil December

"조기 수확"이란 무엇입니까?
Brad Thomas

1
@BradThomas 프로그램이 완료되기 전에 가비지 수집기가 메모리를 수집 할 때.
bbum

21

id는 목적 C 객체에 대한 포인터이며, void *는 모든 것에 대한 포인터입니다.

id는 알 수없는 mthod 호출과 관련된 경고도 해제합니다. 예를 들면 다음과 같습니다.

[(id)obj doSomethingWeirdYouveNeverHeardOf];

알 수없는 방법에 대한 일반적인 경고를 제공하지 않습니다. 물론 obj가 nil이거나 실제로 해당 메소드를 구현하지 않는 한 런타임에 예외가 발생합니다.

종종 당신은 사용해야 NSObject*id<NSObject>에 우선 id당신이 안전하게에 / 해제 / 오토 릴리즈를 유지하는 등의 방법을 사용할 수 있도록 적어도 확인한다에 반환되는 객체는, 코코아 객체입니다.


2
메소드 호출의 경우, 대상 메소드가 어디에도 선언되지 않은 경우 (id) 유형의 대상이 경고를 생성합니다. 따라서 귀하의 예에서 doSomethingWeirdYouveNeverHeardOf는 경고가 발생하지 않도록 어딘가에 선언되었을 것입니다.
bbum

wuite가 맞습니다. 더 좋은 예는 storagePolicy와 같은 것입니다.
피터 N 루이스

@PeterNLewis Often you should use NSObject*대신에 동의하지 않습니다 id. 지정 NSObject*하면 실제로 객체가 NSObject라고 명시 적으로 말합니다. 객체에 대한 모든 메소드 호출은 경고가 발생하지만 해당 객체가 실제로 메소드 호출에 응답하는 한 런타임 예외는 없습니다. 경고는 분명히 성가 시므로 id더 좋습니다. 예를 들어 id<MKAnnotation>,이 경우 객체가 무엇이든 관계없이 MKAnnotation 프로토콜을 준수해야합니다.
pnizzle

1
id <MKAnnotation>을 사용하려는 경우 NSObject <MKAnnotation> *도 사용할 수 있습니다. 그러면 MKAnnotation의 모든 메소드와 NSObject의 모든 메소드 (예 : 일반 NSObject 루트 클래스 계층의 모든 객체)를 사용할 수 있으며 다른 경고는 없습니다. 런타임 충돌.
피터 N 루이스

8

메소드에 리턴 유형이 id있으면 Objective-C 오브젝트를 리턴 할 수 있습니다.

void 즉, 메소드는 아무것도 반환하지 않습니다.

void *그냥 포인터입니다. 포인터가 가리키는 주소의 내용을 편집 할 수 없습니다.


2
메소드의 반환 값에 적용되므로 대부분 그렇습니다. 변수 또는 인수 선언에 적용되는 것은 아닙니다. 또한 내용을 읽고 쓰려면 항상 (void *)를 더 구체적인 유형으로 캐스팅 할 수 있습니다.
bbum

8

idObjective-C 객체에 대한 포인터입니다. 아무것도void * 가리키는 포인터 입니다. 대신에 사용할 수 있지만 컴파일러 경고가 전혀 발생하지 않으므로 권장하지 않습니다.void *id

stackoverflow.com/questions/466777/whats-the-difference-beclaring-a-variable-id-and-nsobjectunixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs 를 참조하십시오. -id.html .


1
좀 빠지는. (void *) 유형 변수는 메소드 호출의 대상이 될 수 없습니다. 컴파일러에서 "경고 : 잘못된 수신자 유형 'void *'"가 발생합니다.
bbum

@bbum : void *형식화 된 변수는 메서드 호출의 대상이 될 수 있습니다. 오류가 아니라 경고입니다. 뿐만 아니라 int i = (int)@"Hello, string!";다음과 같이 할 수 있습니다 printf("Sending to an int: '%s'\n", [i UTF8String]);. 오류가 아니라 경고 (정확히 권장되거나 휴대용이 아님)입니다. 그러나 이런 일을 할 수있는 이유는 모두 기본적인 C입니다.
johne

1
죄송합니다. 당신이 올바른지; 오류가 아니라 경고입니다. 나는 항상 경고를 항상 어디서나 오류로 취급합니다.
bbum

4
/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

위의 코드는 objc.h의 코드이므로 id는 objc_object 구조체의 인스턴스이며 isa 포인터는 Objective C Class 객체와 바인딩 할 수 있지만 void *는 형식화되지 않은 포인터입니다.


2

내 이해는 id가 객체에 대한 포인터를 나타내는 반면 void *는 사용하려는 유형으로 캐스팅하는 한 실제로 무엇이든 가리킬 수 있다는 것입니다.


(void *)에서 id를 포함한 일부 객체 유형으로 캐스팅하는 경우 잘못했을 가능성이 큽니다. 그렇게하는 데는 이유가 있지만 디자인 결함을 거의, 거의 또는 거의 나타내지 않습니다.
bbum

1
"그렇게해야 할 이유가 있지만 그 사이에는별로 없다"고 인용하십시오. 상황에 따라 다릅니다. 그러나 나는 어떤 맥락없이 "당신이 잘못하고 있다고 생각하는 것"과 같은 담요 진술을하지 않을 것입니다.
hhafez

나는 담요 진술을 할 것이다. void *를 사용하여 잘못된 유형으로 캐스팅하여 너무 많은 저주받은 버그를 찾아 내고 수정해야했습니다. 한 가지 예외는 콜백 설정 API와 콜백 수신 사이에 컨텍스트가 그대로 유지되는 void * 컨텍스트 인수를 취하는 콜백 기반 API입니다.
bbum

0

이미 말한 것 외에도 컬렉션과 관련된 객체와 포인터 사이에는 차이가 있습니다. 예를 들어, NSArray에 무언가를 넣으려면 "id"유형의 객체가 필요하며 "void *"유형의 원시 데이터 포인터를 사용할 수 없습니다. 컬렉션 내에서 사용하기 위해 "id"형식 [NSValue valueWithPointer:rawData]으로 변환하는 void *rawDdata데 사용할 수 있습니다 . 일반적으로 "id"는 더 유연하며 연결된 객체와 관련된 의미가 더 많습니다. Objective C의 id 유형을 설명하는 더 많은 예제가 있습니다 .

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