ObjectiveC에서 변수 위치 선언 / 정의?


113

iOS 앱과 객관적인 C 작업을 시작한 이래로 저는 변수를 선언하고 정의 할 수있는 여러 위치에 대해 정말 당황했습니다. 한편으로는 전통적인 C 접근 방식이 있고 다른 한편으로는 OO를 추가하는 새로운 ObjectiveC 지시문이 있습니다. 여러분은 제가이 위치를 변수에 사용하고 아마도 현재의 이해를 수정하고자하는 모범 사례와 상황을 이해하도록 도와 주실 수 있습니까?

다음은 샘플 클래스 (.h 및 .m)입니다.

#import <Foundation/Foundation.h>

// 1) What do I declare here?

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

// 3) class-specific method / property declarations

@end

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

@implementation SampleClass
{
    // 6) define ivars
}

// 7) define methods and synthesize properties from both public and private
//    interfaces

@end
  • 1과 4에 대한 나의 이해는 그것들은 클래스의 개념을 전혀 이해하지 못하는 C 스타일의 파일 기반 선언과 정의이기 때문에 C에서 어떻게 사용되는지 정확히 사용해야한다는 것입니다. 이전에 정적 변수 기반 싱글 톤을 구현하는 데 사용되었습니다. 내가 놓친 다른 편리한 용도가 있습니까?
  • iOS 작업에서 얻은 견해는 ivar가 @synthesize 지시문 외부에서 완전히 단계적으로 제거되었으므로 대부분 무시할 수 있다는 것입니다. 그럴까요?
  • 5와 관련하여 : 왜 개인 인터페이스에서 메서드를 선언하고 싶습니까? 내 개인 클래스 메서드는 인터페이스에 선언없이 잘 컴파일되는 것 같습니다. 대부분 가독성을위한 것입니까?

감사합니다, 여러분!

답변:


154

나는 당신의 혼란을 이해할 수 있습니다. 특히 최근 Xcode 및 새로운 LLVM 컴파일러 업데이트로 인해 ivar 및 속성을 선언 할 수있는 방식이 변경되었습니다.

"현대적인"Objective-C ( "오래된"Obj-C 2.0에서) 이전에는 선택의 여지가 많지 않았습니다. 중괄호 사이의 헤더에서 선언하는 데 사용되는 인스턴스 변수 { }:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

구현에서만 이러한 변수에 액세스 할 수 있지만 다른 클래스에서는 액세스 할 수 없습니다. 이를 위해 다음과 같은 접근 자 메서드를 선언해야합니다.

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

이렇게하면 일반적인 대괄호 구문을 사용하여 메시지 (호출 메서드)를 사용하여 다른 클래스에서도이 인스턴스 변수를 가져오고 설정할 수 있습니다.

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

수동으로 선언하고 모든 접근 방법은 매우 짜증나 구현 때문에 @property@synthesize자동 접근 방법을 생성하기 위해 도입되었다 :

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

결과는 훨씬 더 명확하고 짧은 코드입니다. 접근 자 메서드가 구현되며 이전과 같이 대괄호 구문을 계속 사용할 수 있습니다. 그러나 또한 점 구문을 사용하여 속성에 액세스 할 수도 있습니다.

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Xcode 4.4부터는 더 이상 인스턴스 변수를 직접 선언 할 필요가 없으며 건너 뛸 수도 @synthesize있습니다. ivar를 선언하지 않으면 컴파일러가이를 추가하고 .NET Framework를 사용하지 않고도 접근 자 메서드를 생성합니다 @synthesize.

자동 생성 된 ivar의 기본 이름은 밑줄로 시작하는 이름 또는 속성입니다. 다음을 사용하여 생성 된 ivar의 이름을 변경할 수 있습니다.@synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

이것은 위의 코드와 똑같이 작동합니다. 호환성을 위해 헤더에서 ivars를 선언 할 수 있습니다. 그러나 속성을 선언하지 않고이를 수행하려는 유일한 이유는 개인 변수를 만드는 것이므로 이제 구현 파일에서도이를 수행 할 수 있으며 이것이 선호되는 방법입니다.

@interface구현 파일 의 블록은 실제로 확장 이며 선언 메서드 (더 이상 필요하지 않음)를 전달하고 속성을 (재) 선언하는 데 사용할 수 있습니다. 예를 들어 readonly헤더에 속성을 선언 할 수 있습니다.

@property (nonatomic, readonly) myReadOnlyVar;

readwriteivar에 대한 직접 액세스뿐만 아니라 속성 구문을 사용하여 설정할 수 있도록 구현 파일에서 다시 선언하십시오 .

변수를 any @interface또는 @implementation블록 외부에서 완전히 선언 하는 경우에는 예, 일반 C 변수이며 정확히 동일하게 작동합니다.


2
좋은 대답! 또한 참고 : stackoverflow.com/questions/9859719/...
nycynik

44

먼저 @DrummerB의 답변을 읽으십시오. 이유와 일반적으로 수행해야하는 작업에 대한 좋은 개요입니다. 이를 염두에두고 구체적인 질문에 대해

#import <Foundation/Foundation.h>

// 1) What do I declare here?

여기에는 실제 변수 정의가 없습니다 (현재 수행중인 작업을 정확히 알고 있지만 절대로 수행하지 않는 경우 기술적으로 합법적입니다). 몇 가지 다른 종류를 정의 할 수 있습니다.

  • typdefs
  • 열거 형
  • externs

Externs는 변수 선언처럼 보이지만 실제로 다른 곳에서 선언하겠다는 약속 일뿐입니다. ObjC에서는 상수를 선언하는 데만 사용해야하며 일반적으로 문자열 상수 만 사용해야합니다. 예를 들면 :

extern NSString * const MYSomethingHappenedNotification;

그런 다음 .m파일에서 실제 상수를 선언합니다.

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

DrummerB가 언급했듯이 이것은 유산입니다. 여기에 아무것도 넣지 마십시오.


// 3) class-specific method / property declarations

@end

네.


#import "SampleClass.h"

// 4) what goes here?

위에서 설명한 외부 상수. 또한 파일 정적 변수는 여기로 갈 수 있습니다. 이는 다른 언어의 클래스 변수와 동일합니다.


@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end


@implementation SampleClass
{
    // 6) define ivars
}

그러나 아주 드물게. 거의 항상 clang (Xcode)이 변수를 생성하도록 허용해야합니다. 예외는 일반적으로 ObjC가 아닌 ivar (Core Foundation 객체, 특히 ObjC ++ 클래스 인 경우 C ++ 객체) 또는 이상한 저장소 의미를 가진 ivar (예 : 어떤 이유로 속성과 일치하지 않는 ivar)에 관한 것입니다.


// 7) define methods and synthesize properties from both public and private
//    interfaces

일반적으로 더 이상 @synthesize해서는 안됩니다. Clang (Xcode)이 당신을 위해 할 것입니다.

지난 몇 년 동안 상황은 훨씬 더 단순 해졌습니다. 부작용은 이제 세 가지 시대 (Fragile ABI, Non-Fragile ABI, Non-Fragile ABI + auto-syntheisze)가 있다는 것입니다. 따라서 이전 코드를 보면 약간 혼란 스러울 수 있습니다. 따라서 단순성에서 발생하는 혼란 : D


궁금한 점이 있지만 명시 적으로 합성하면 안되는 이유는 무엇입니까? 특히 일부 속성에는 접근자가 합성되고 일부는 사용자 지정 구현이있을 때 내 코드를 더 쉽게 이해할 수 있기 때문에 그렇게합니다. 합성에 익숙하기 때문입니다. 명시 적 합성에 단점이 있습니까?
Metabble

문서화로 사용할 때의 문제점은 실제로 문서화하지 않는다는 것입니다. synthesize를 사용 했음에도 불구하고 접근자를 하나 또는 둘 다 재정의했을 수 있습니다. 합성 라인에서 정말 유용한 것을 말할 방법이 없습니다. 문서가없는 것보다 더 나쁜 것은 잘못된 문서입니다. 놔둬.
Rob Napier

3
# 6이 희귀 한 이유는 무엇입니까? 이것이 개인 변수를 얻는 가장 쉬운 방법이 아닙니까?
pfrank 2013 년

사유 재산을 얻는 가장 쉽고 가장 좋은 방법은 # 5입니다.
Rob Napier

1
@RobNapier 가끔 @ synthesize를 사용해야합니다 (예 : 속성이 읽기 전용 인 경우 접근자가 재정의 된 경우)
Andy

6

나는 또한 꽤 새롭기 때문에 아무 것도 망치지 않기를 바랍니다.

1 & 4 : C 스타일 전역 변수 : 파일 전체 범위가 있습니다. 둘 사이의 차이점은 파일 전체가 넓기 때문에 첫 번째는 헤더를 가져 오는 모든 사람이 사용할 수 있지만 두 번째는 그렇지 않다는 것입니다.

2 : 인스턴스 변수. 대부분의 인스턴스 변수는 속성을 사용하여 접근자를 통해 합성되고 검색 / 설정됩니다. 이는 메모리 관리를 멋지고 간단하게 만들뿐만 아니라 이해하기 쉬운 점 표기법을 제공하기 때문입니다.

6 : 구현 ivar는 다소 새로운 것입니다. 공개 헤더에 필요한 것만 노출하고 싶지만 하위 클래스는 AFAIK를 상속하지 않기 때문에 비공개 ivar를 배치하는 것이 좋습니다.

3 & 7 : 공용 메서드 및 속성 선언 후 구현.

5 : 개인 인터페이스. 나는 물건을 깨끗하게 유지하고 일종의 블랙 박스 효과를 만들기 위해 항상 비공개 인터페이스를 사용합니다. 그들이 그것에 대해 알 필요가 없다면 거기에 두십시오. 나는 또한 가독성을 위해 그것을합니다. 다른 이유가 있는지 모르겠습니다.


1
당신이 아무것도 망쳐 놓았다고 생각하지 마십시오 :) 몇 가지 코멘트-# 4와 함께 # 1 & # 4 esp 종종 정적 저장 변수를 보게됩니다. # 1 종종 외부 스토리지가 지정되고 # 4에서 할당 된 실제 스토리지가 표시됩니다. # 2) 어떤 이유로 든 서브 클래스가 필요로하는 경우에만 일반적으로. # 5 더 이상 private 메서드를 전달할 필요가 없습니다.
Carl Veazey 2012 년

네, 방금 선언문을 직접 확인했습니다. 하나의 private 메서드가 앞으로 선언없이 정의 된 다른 메서드를 호출하면 경고를 주곤 했죠? 경고하지 않았을 때 좀 놀랐습니다.
Metabble

네, 컴파일러의 새로운 부분입니다. 그들은 최근에 많은 발전을 이루었습니다.
Carl Veazey 2012 년

6

이것은 Objective-C에서 선언 된 모든 종류의 변수의 예입니다. 변수 이름은 액세스를 나타냅니다.

파일 : Animal.h

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

파일 : Animal.m

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

iNotVisible 변수는 다른 클래스에서 볼 수 없습니다. 이것은 가시성 문제이므로 선언 @property하거나 @public변경하지 않습니다.

생성자 내에서 부작용을 피하기 위해 @property대신 밑줄 을 사용하여 선언 된 변수에 액세스하는 것이 좋습니다 self.

변수에 액세스 해 보겠습니다.

파일 : Cow.h

#import "Animal.h"
@interface Cow : Animal
@end

파일 : Cow.m

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

런타임을 사용하여 보이지 않는 변수에 계속 액세스 할 수 있습니다.

파일 : Cow.m (2 부)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

보이지 않는 변수에 접근 해 봅시다.

파일 : main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

이것은 인쇄

iMadeVisible 
iMadeVisible2 
iMadeVisible3

_iNotVisible2하위 클래스 전용 인 백업 ivar에 액세스 할 수있었습니다 . Objective-C에서는 모든 변수를 읽거나 설정할 수 있으며,으로 표시된 변수 @private도 예외없이 읽을 수 있습니다 .

나는 서로 다른 새이기 때문에 관련 객체 또는 C 변수를 포함하지 않았습니다. C 변수의 경우 외부에서 정의 된 모든 변수 @interface X{}또는 @implementation X{}파일 범위 및 정적 저장소가있는 C 변수입니다.

메모리 관리 속성, 읽기 전용 / 읽기 쓰기, getter / setter 속성에 대해서는 논의하지 않았습니다.

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