@ 클래스 대 #import


709

클래스 A가 ClassB 헤더를 포함해야하고 클래스 B가 순환 포함을 피하기 위해 클래스 A 헤더를 포함 해야하는 경우 이벤트에서 순방향 클래스 선언을 사용해야한다는 것을 이해합니다. 또한 포함이 한 번만 발생하도록 #import간단한 것임을 이해합니다 ifndef.

내 질문은 이것입니다 : 하나는 언제 사용 #import하고 언제 사용 @class합니까? 때로는 @class선언을 사용 하면 다음과 같은 일반적인 컴파일러 경고가 표시됩니다.

warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.

@class전진 선언을 제거 #import하고 컴파일러가 나에게주는 경고를 침묵시키는 것을 던지는 것보다 이것을 이해 하고 싶습니다.


10
순방향 선언은 컴파일러에게 "이봐, 나는 당신이 인식하지 못하는 것을 선언하고 있다는 것을 알고 있지만, @MyClass라고 말하면 구현에서 #import를 가져올 것"이라고 약속한다.
JoeCortopassi

답변:


754

이 경고가 표시되면

경고 : 수신자 'MyCoolClass'는 순방향 클래스이며 해당 @ 인터페이스가 존재하지 않을 수 있습니다

#import파일 이 필요 하지만 구현 파일 (.m)에서이를 수행하고 @class헤더 파일에서 선언을 사용할 수 있습니다.

@class(보통) #import파일 의 필요성을 제거하지 않고 정보가 유용한 곳으로 요구 사항을 더 가깝게 이동시킵니다.

예를 들어

이라고 말하면 @class MyCoolClass컴파일러는 다음과 같은 것을 볼 수 있습니다.

MyCoolClass *myObject;

MyCoolClass유효한 클래스 이외의 것에 대해 걱정할 필요가 없으며 포인터 (실제로는 포인터)를위한 공간을 예약해야합니다. 따라서 헤더 @class에서 시간의 90 %이면 충분합니다.

그러나 myObject의 멤버 를 작성하거나 액세스 해야하는 경우 컴파일러에게 해당 메소드가 무엇인지 알려 주어야합니다. 이 시점에서 (아마도 구현 파일에서) #import "MyCoolClass.h"컴파일러에게 "이것은 클래스입니다"이외의 추가 정보를 알려 주어야합니다 .


5
좋은 답변, 감사합니다. 나중에 참조하기 위해 : 파일 @class에서 무언가를 처리 .h하지만 #import.m에서 잊어 버리고 @classed 객체 의 메소드에 액세스하고 다음과 같은 경고 를 받으십시오 warning: no -X method found.
Tim

24
@class 대신 #import를 사용해야하는 경우는 .h 파일에 클래스 인터페이스에 필요한 데이터 유형 또는 기타 정의가 포함되어있는 경우입니다.
Ken Aspeslagh

2
여기에 언급되지 않은 또 다른 큰 장점은 빠른 컴파일입니다.
MartinMoizard

@BenGottlieb "myCoolClass"에서 'm'은 대문자가 아니어야합니까? "MyCoolClass"에서와 같이?
Basil Bourque

182

세 가지 간단한 규칙 :

  • #import헤더 파일 ( .h파일) 에는 수퍼 클래스 및 채택 된 프로토콜 만 있습니다.
  • #import모든 클래스와 프로토콜의 경우 구현시 메시지를 .m파일로 보냅니다 .
  • 다른 모든 것에 대한 선언을 전달하십시오.

구현 파일에서 선언을 전달하면 뭔가 잘못되었을 수 있습니다.


22
헤더 파일에서 클래스가 채택한 프로토콜을 정의하는 항목을 #import로 가져와야 할 수도 있습니다.
Tyler

h 인터페이스 파일 또는 m 구현 파일에서 #import를 선언하는 데 차이가 있습니까?
Samuel G

그리고 #import 클래스에서 인스턴스 변수를 사용하는 경우
user151019

1
@Mark-규칙 # 1에 포함되어 있으며, 수퍼 클래스에서만 ivar에 액세스합니다.
PeyloW

@ 타일러 왜 프로토콜 선언을 전달하지 않습니까?
JoeCortopassi

110

ADC 에 대한 Objective-C 프로그래밍 언어 문서를보십시오

클래스 정의하기 섹션에서 | 이것이 수행되는 이유를 설명하는 클래스 인터페이스 :

@class 지시문은 컴파일러와 링커에서 볼 수있는 코드의 양을 최소화하므로 클래스 이름을 전달하는 가장 간단한 방법입니다. 간단하기 때문에 다른 파일을 가져 오는 파일을 가져올 때 발생할 수있는 잠재적 인 문제를 피할 수 있습니다. 예를 들어, 한 클래스가 다른 클래스의 정적으로 유형이 지정된 인스턴스 변수를 선언하고 두 인터페이스 파일이 서로 가져 오면 클래스가 올바르게 컴파일되지 않을 수 있습니다.

이게 도움이 되길 바란다.


48

필요한 경우 헤더 파일에 전달 선언을 #import사용하고 구현에 사용중인 클래스의 헤더 파일을 사용하십시오. 즉, 항상 #import구현에 사용하는 파일이며 헤더 파일에서 클래스를 참조 해야하는 경우 전달 선언도 사용하십시오.

이에 대한 예외#import헤더 파일에서 상속하는 클래스 또는 공식 프로토콜 이어야한다는 것입니다 (이 경우 구현에서 가져올 필요가 없습니다).


24

일반적인 방법은 헤더 파일에서 @class를 사용하지만 (여전히 #supersuperclass를 가져와야 함) 구현 파일에서 #import를 사용해야합니다. 이것은 순환 포함을 피할 것이고, 그것은 효과가 있습니다.


2
#import가 하나의 인스턴스 만 가져 오기 때문에 #Import보다 낫다고 생각했습니다.
Matthew Schinckel 1

2
진실. 그것이 순환 포함 또는 잘못된 순서에 관한 것인지는 모르지만 그 규칙에서 벗어나 모험을했습니다 (헤더에서 하나의 가져 오기, 하위 클래스의 구현에서는 더 이상 가져 오기가 더 이상 필요하지 않음). 결론은 그 규칙을 따르고 컴파일러는 행복 할 것입니다.
Steph Thirion

1
현재 문서는 그 말 #import"그것은 확실히 같은 파일을 두 번 이상 포함되지 않습니다 것을 만드는 것을 제외하고, C의 #include 지시문 같다." 따라서 이것에 따르면 #import순환 포함을 처리 하므로 @class지시문이 특히 도움이되지 않습니다.
Eric

24

또 다른 장점 : 빠른 컴파일

헤더 파일을 포함하는 경우 헤더 파일을 변경하면 현재 파일도 컴파일되지만 클래스 이름이로 포함 된 경우에는 그렇지 않습니다 @class name. 물론 소스 파일에 헤더를 포함해야합니다


18

내 질문은 이것입니다. 언제 #import를 사용하고 언제 @class를 사용합니까?

간단한 대답 : 귀하 #import또는 #include신체적 의존성이있을 때. 그렇지 않으면, 당신은 앞으로 선언을 사용 ( @class MONClass, struct MONStruct, @protocol MONProtocol).

신체적 의존성에 대한 일반적인 예는 다음과 같습니다.

  • C 또는 C ++ 값 (포인터 또는 참조는 물리적 종속성이 아님) CGPointivar 또는 속성으로 a 를 사용하는 경우 컴파일러에서의 선언을 확인해야합니다 CGPoint.
  • 당신의 슈퍼 클래스.
  • 사용하는 방법.

@class 선언을 사용하는 경우 때때로 다음과 같은 일반적인 컴파일러 경고가 표시됩니다. "경고 : 수신자 'FooController'는 순방향 클래스이며 해당 @interface가 없을 수 있습니다."

컴파일러는 이와 관련하여 실제로 매우 관대합니다. 힌트 (예 : 위와 같은)를 제거하지만 스택을 무시하고 #import올바르게 처리 하지 않으면 스택을 쉽게 휴지통에 버릴 수 있습니다 . 비록 (IMO)하더라도 컴파일러는 이것을 강제하지 않습니다. ARC에서 컴파일러는 참조 카운팅을 담당하므로 더 엄격합니다. 무슨 일이 일어나는지는 컴파일러가 알 수없는 메소드를 만나면 기본값으로 돌아갑니다. 모든 반환 값과 매개 변수는로 가정합니다 id. 따라서 코드베이스의 모든 경고를 근절해야합니다. 이는 물리적 종속성으로 간주되어야하기 때문입니다. 이것은 선언되지 않은 C 함수를 호출하는 것과 유사합니다. C를 사용하면 매개 변수는로 간주됩니다 int.

전방 선언을 선호하는 이유는 최소한의 의존성이 있기 때문에 요인으로 빌드 시간을 줄일 수 있기 때문입니다. 정방향 선언을 사용하면 컴파일러는 이름이 있음을 확인하고 물리적 종속성이 없을 때 클래스 선언이나 모든 종속성을 보지 않고도 프로그램을 올바르게 구문 분석하고 컴파일 할 수 있습니다. 깨끗한 빌드는 시간이 덜 걸립니다. 증분 빌드는 시간이 덜 걸립니다. 물론, 필요한 모든 헤더가 결과적으로 모든 번역에 표시되도록하는 데 약간의 시간이 더 걸리지 만, 빌드 시간이 단축됩니다 (프로젝트가 작지 않다고 가정).

#import또는 #include대신 사용 하면 필요한 것보다 컴파일러에서 더 많은 작업을 수행합니다. 복잡한 헤더 종속성도 도입하고 있습니다. 이것을 무차별 알고리즘에 비유 할 수 있습니다. 당신이 때 #import, 당신은 메모리, 디스크 분석하고 소스를 컴파일하는 I / O 및 CPU를 많이 필요로 불필요한 정보의 톤에 드래그하고 있습니다.

ObjC는 NSObject타입이 결코 값이 아니기 때문에 의존성 측면에서 C 기반 언어에 이상적입니다. NSObject타입은 항상 참조 카운트 포인터입니다. 따라서 물리적 종속성이 거의 없기 때문에 프로그램의 종속성을 적절하게 구성하고 가능한 경우 앞으로 컴파일하면 엄청나게 빠른 컴파일 시간을 피할 수 있습니다. 종속성을 추가로 최소화하기 위해 클래스 확장에서 속성을 선언 할 수도 있습니다. 이는 대규모 시스템의 경우 큰 보너스입니다. 대규모 C ++ 코드베이스를 개발 한 경우의 차이점을 알게 될 것입니다.

따라서 가능한 경우 전달을 사용하고 #import물리적 의존성이있는 곳을 사용하는 것이 좋습니다 . 물리적 의존성을 의미하는 경고 또는 다른 경고가 표시되면 모두 수정하십시오. 수정 사항은 #import구현 파일에 있습니다.

라이브러리를 빌드 할 때 일부 인터페이스를 그룹으로 분류 할 #import수 있습니다.이 경우 물리적 종속성이 도입 된 라이브러리 (예 :)가 #import <AppKit/AppKit.h>됩니다. 이것은 의존성을 유발할 수 있지만, 라이브러리 관리자는 종종 필요에 따라 물리적 의존성을 처리 할 수 ​​있습니다. 기능을 도입하면 빌드에 미치는 영향을 최소화 할 수 있습니다.


BTW 일을 설명하는 좋은 노력. . 그러나 그들은 상당히 복잡해 보입니다.
Ajay Sharma 2012

NSObject types are never values -- NSObject types are always reference counted pointers.완전히 사실이 아닙니다. 블록은 대답에 허점을 던집니다.
Richard J. Ross III

@ RichardJ.RossIII… 그리고 GCC는 값을 선언하고 사용할 수 있도록 허용하지만 clang은이를 금지합니다. 물론 포인터 뒤에 값이 있어야합니다.
저스틴

11

"이렇게합니까"가 많이 표시되지만 "왜"에 대한 답변이 표시되지 않습니다.

따라서 헤더에서 @class를 사용해야하고 구현에서만 #import를 가져와야합니까? 항상 @class #import 를 수행하여 작업을 배가 시키고 있습니다. 상속을 사용하지 않는 한 이 경우 단일 @class에 대해 #import를 여러 번 가져옵니다. 그런 다음 갑자기 더 이상 선언에 액세스 할 필요가 없다고 판단되면 여러 개의 다른 파일에서 제거해야합니다.

#import의 특성상 동일한 파일을 여러 번 가져 오는 것은 문제가되지 않습니다. 컴파일 성능도 실제로 문제가되지 않습니다. 그렇다면 우리가 가진 모든 헤더 파일에서 #importa Cocoa / Cocoa.h 등이되지 않을 것입니다.


1
왜해야하는지에 대한 문서의 예제는 위의 Abizem의 답변을 참조하십시오. 다른 클래스의 인스턴스 변수로 서로 가져 오는 두 개의 클래스 헤더가있는 경우 방어 프로그래밍.
jackslash 10

7

우리가 이걸하면

@interface Class_B : Class_A

즉, Class_A를 Class_B로 상속한다는 의미입니다. Class_B에서는 class_A의 모든 변수에 액세스 할 수 있습니다.

우리가 이것을하고 있다면

#import ....
@class Class_A
@interface Class_B

여기서는 프로그램에서 Class_A를 사용하고 있다고 말하지만 Class_B에서 Class_A 변수를 사용하려면 .m 파일에서 Class_A를 #import (객체를 만들고 함수와 변수를 사용해야 함)해야합니다.


5

파일 종속성 및 #import & @class에 대한 추가 정보는 다음을 확인하십시오.

http://qualitycoding.org/file-dependencies/ itis good article

기사 요약

헤더 파일로 가져 오기 :

  • 상속하는 수퍼 클래스와 구현중인 프로토콜을 가져옵니다.
  • 마스터 헤더가있는 프레임 워크에서 제공되지 않는 한 다른 모든 것을 앞으로 선언하십시오.
  • 다른 모든 #import를 제거하십시오.
  • 종속성을 줄이기 위해 자체 헤더에 프로토콜을 선언하십시오.
  • 전달 선언이 너무 많습니까? 당신은 큰 클래스가 있습니다.

구현 파일에서 가져 오기 :

  • 사용되지 않은 cruft #imports를 제거하십시오.
  • 메소드가 다른 객체에 위임하고 반환되는 것을 반환하는 경우 #importing 대신 해당 객체를 전달 선언합니다.
  • 모듈을 포함 시키면 연속적인 종속성 레벨 이후에 레벨을 포함시켜야하는 경우 라이브러리가 되려는 클래스 세트가있을 수 있습니다. 마스터 헤더를 사용하여 별도의 라이브러리로 빌드하여 모든 것을 단일 사전 빌드 청크로 가져올 수 있습니다.
  • #imports가 너무 많습니까? 당신은 큰 클래스가 있습니다.

3

내가 개발할 때, 나는 결코 문제를 일으키지 않는 것을 세 가지 명심해야합니다.

  1. 수퍼 클래스 가져 오기
  2. 부모 수업 가져 오기 (자녀와 부모가있는 경우)
  3. 프레임 워크 및 라이브러리와 같이 프로젝트 외부에서 클래스 가져 오기

다른 모든 클래스 (프로젝트 자체의 하위 클래스 및 하위 클래스)의 경우 클래스를 통해 선언합니다.


3

아직 가져 오지 않은 헤더 파일의 변수 또는 속성을 선언하려고하면 컴파일러가이 클래스를 모른다는 오류가 발생합니다.

당신의 첫 번째 생각은 아마입니다 #import.
경우에 따라 문제가 발생할 수 있습니다.

예를 들어 헤더 파일이나 구조체 또는 이와 유사한 C 메서드를 여러 번 가져 와서는 안되기 때문에 많은 C 메서드를 구현하는 경우가 있습니다.

따라서 다음과 같이 컴파일러에 알릴 수 있습니다 @class.

나는 당신이 그 수업을 모른다는 것을 알고 있지만 존재합니다. 다른 곳으로 가져 오거나 구현할 예정입니다.

기본적으로이 클래스가 구현 될지 확실하지 않더라도 컴파일러에게 시스템 종료 및 컴파일을 지시합니다.

당신은 일반적으로 사용 #import에서 하는 .m@class.H 파일.


0

컴파일러가 오류를 표시하지 못하도록 선언을 전달하십시오.

컴파일러는 선언하기 위해 헤더 파일에 사용한 이름의 클래스가 있음을 알게됩니다.


좀 더 구체적으로 말씀해 주시겠습니까?
Sam Spencer

0

컴파일러는 컴파일러가 구현을 알아야하는 방식으로 해당 클래스를 사용하려는 경우에만 불만을 표시합니다.

전의:

  1. 클래스에서 클래스를 파생 시키거나
  2. 해당 클래스의 객체를 멤버 변수로 사용하려는 경우 (드물지만).

포인터로 사용하려고하면 불평하지 않습니다. 물론 객체를 인스턴스화하려면 클래스 내용을 알아야하기 때문에 구현 파일에서 #import (클래스의 객체를 인스턴스화하는 경우)해야합니다.

참고 : #import는 #include와 다릅니다. 즉, 순환 가져 오기라는 것은 없습니다. 가져 오기는 일종의 정보를 위해 컴파일러가 특정 파일을 살펴보기위한 요청입니다. 해당 정보가 이미 사용 가능한 경우 컴파일러는 해당 정보를 무시합니다.

이것을 시도해보십시오. Ah에서 Bh로 가져오고 Bh에서 Ah로 가져 오십시오. 문제 나 불만이 없으며 잘 작동합니다.

@class를 사용하는 경우

헤더에서 헤더를 가져오고 싶지 않은 경우에만 @class를 사용하십시오. 이것은 당신이 그 수업이 무엇인지 알지 못하는 경우 일 수 있습니다. 해당 클래스의 헤더가 아직없는 경우.

예를 들어 두 개의 라이브러리를 작성하고있을 수 있습니다. 하나의 클래스는 A라고 부르며 하나의 라이브러리에 존재합니다. 이 라이브러리에는 두 번째 라이브러리의 헤더가 포함됩니다. 해당 헤더에는 A의 포인터가있을 수 있지만 다시 사용할 필요는 없습니다. 라이브러리 1을 아직 사용할 수없는 경우 @class를 사용하면 라이브러리 B가 차단되지 않습니다. 그러나 Ah를 가져 오려고하면 라이브러리 2의 진행이 차단됩니다.


0

@class를 컴파일러에게 "믿어주세요. 이것이 존재합니다"라고 말하는 것으로 생각하십시오.

#import를 복사-붙여 넣기로 생각하십시오.

여러 가지 이유로 가져 오기 수를 최소화하려고합니다. 연구가 없다면, 가장 먼저 염두에 두어야 할 것은 컴파일 시간을 줄이는 것입니다.

클래스에서 상속 받으면 단순히 순방향 선언을 사용할 수 없습니다. 선언하는 클래스가 파일이 정의 된 방식을 알 수 있도록 파일을 가져와야합니다.


0

이것은 @class가 필요한 예제 시나리오입니다.

동일한 클래스의 데이터 유형을 가진 매개 변수가있는 헤더 파일 내에 프로토콜을 작성하려는 경우 @class를 사용할 수 있습니다. 프로토콜을 별도로 선언 할 수도 있습니다. 이것은 단지 예일뿐입니다.

// DroneSearchField.h

#import <UIKit/UIKit.h>
@class DroneSearchField;
@protocol DroneSearchFieldDelegate<UITextFieldDelegate>
@optional
- (void)DroneTextFieldButtonClicked:(DroneSearchField *)textField;
@end
@interface DroneSearchField : UITextField
@end
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.