Objective-C에서 클래스에 대한 개인 메소드를 정의하는 가장 좋은 방법


355

방금 Objective-C 프로그래밍을 시작했으며 Java 배경을 가지고 Objective-C 프로그램을 작성하는 사람들이 개인 메소드를 어떻게 처리하는지 궁금합니다.

나는 몇 가지 규칙과 습관이있을 수 있음을 이해하고 Objective-C에서 개인 방법을 다루는 사람들이 사용하는 최고의 기술을 모은 것으로이 질문에 대해 생각합니다.

게시 할 때 접근 방식에 대한 인수를 포함하십시오. 왜 좋은가요? 어떤 결점 (알고 있음)이 있으며 어떻게 처리합니까?


내 결과는 지금까지.

MyClass.m 파일에 정의 된 범주 (예 : MyClass (Private)) 를 사용 하여 개인용 메서드를 그룹화 할 수 있습니다.

이 방법에는 두 가지 문제가 있습니다.

  1. Xcode (및 컴파일러?)는 해당 @implementation 블록에서 개인 범주의 모든 메소드를 정의하는지 확인하지 않습니다.
  2. MyClass.m 파일의 시작 부분에 개인 범주를 선언하는 @interface를 넣어야합니다. 그렇지 않으면 "self는"privateFoo "메시지에 응답하지 않을 수 있습니다.

첫 번째 문제는 빈 범주 [예 : MyClass ()]로 해결할 수 있습니다 .
두 번째는 나를 많이 귀찮게합니다. 파일의 끝 부분에서 개인 메소드가 구현되고 정의되는 것을보고 싶습니다. 그게 가능한지 모르겠습니다.


1
사람들은이 질문이 흥미로울 것입니다 : stackoverflow.com/questions/2158660/…
bbum

4
private 메소드의 선언을 생략 하지 않겠습니까?
ma11hew28

답변:


436

다른 사람들이 이미 말했듯이 Objective-C의 개인 메소드와 같은 것은 없습니다. 그러나 Objective-C 2.0 (Mac OS X Leopard, iPhone OS 2.0 이상)을 시작으로 이름이 비어있는 (예 : Class Extension@interface MyClass () ) 범주를 만들 수 있습니다 . 클래스 확장의 고유 한 점은 메소드 구현 이 공개 메소드 와 동일해야한다는 것입니다 . 그래서 수업을 다음과 같이 구성합니다.@implementation MyClass

.h 파일에서 :

@interface MyClass {
    // My Instance Variables
}

- (void)myPublicMethod;

@end

그리고 .m 파일에서 :

@interface MyClass()

- (void)myPrivateMethod;

@end

@implementation MyClass

- (void)myPublicMethod {
    // Implementation goes here
}

- (void)myPrivateMethod {
    // Implementation goes here
}

@end

이 접근법의 가장 큰 장점은 메소드 구현을 (때로는 임의의) 공개 / 개인 구별이 아닌 기능별로 그룹화 할 수 있다는 것입니다.


8
"MYClass가 예외 / 오류가 아닌 '-myPrivateMethod-에 응답하지 않을 수 있습니다"를 생성합니다.
Özgür

2
이것은 실제로 Apple의 상용구 코드에 나타나기 시작합니다. ++
Chris Trahey 2018 년

75
LLVM 4 컴파일러 이상에서는이 작업을 수행 할 필요조차 없습니다. 클래스 확장에 넣지 않고도 구현 내에서 정의 할 수 있습니다.
Abizern

1
@Comptrol에서 언급 한 경고가 표시되면 다른 메소드를 호출하는 대신 메소드를 정의했기 때문입니다 (앤디의 답변 참조). 위험에 따라 이러한 경고를 무시합니다. 나는 이런 실수를했고 컴파일러 if (bSizeDifference && [self isSizeDifferenceSignificant:fWidthCombined])...는 다음 과 같은 호출을 중첩 할 때까지 문제없이 혼란에 빠졌다 . 그런 다음 fWidthCombined는 항상 0으로 전달되었다.
Wienke

6
@Wienke 더 이상 주문에 대해 걱정할 필요가 없습니다. LLVM의 최신 버전은 호출 된 위치 아래에 표시 되더라도 메소드를 찾습니다.
Steve Waddicor

52

런타임에서 사용할 구현을 해결할 수있는 경우 Objective-C에는 실제로 "비공개 메소드"가 없습니다. 그러나 문서화 된 인터페이스의 일부가 아닌 메소드가 없다고 말하는 것은 아닙니다. 그 방법들에 대해서는 카테고리가 괜찮다고 생각합니다. @interface포인트 2와 같이 .m 파일의 맨 위에 넣기보다는 자체 .h 파일에 넣습니다. 내가 따르는 컨벤션 (그리고 다른 곳에서 보았을 때, Xcode가 자동으로 지원하는 애플 컨벤션이라고 생각합니다)은 파일을 클래스와 카테고리 다음에 +로 구분하여 이름을 지정하는 것 @interface GLObject (PrivateMethods)입니다 GLObject+PrivateMethods.h. 헤더 파일을 제공하는 이유는 유닛 테스트 클래스에서이를 가져올 수 있기 때문입니다. :-).

그건 그렇고, .m 파일의 끝 부분에서 메소드를 구현 / 정의하는 경우 .m 파일의 맨 아래에 카테고리를 구현하여 카테고리로 수행 할 수 있습니다.

@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end

또는 클래스 확장명 ( "빈 범주"라고 함)을 사용하는 경우 해당 메소드를 마지막에 정의하십시오. Objective-C 메소드는 구현에서 임의의 순서로 정의하고 사용할 수 있으므로 파일 끝에 "비공개"메소드를 두는 것을 막을 수있는 것은 없습니다.

클래스 확장을 사용하더라도 GLObject+Extension.h"친구"또는 "보호 된"가시성을 모방하여 필요한 경우 이러한 메소드를 사용할 수 있도록 종종 별도의 헤더 ( )를 작성합니다 .

이 답변은 원래 작성되었으므로 clang 컴파일러는 Objective-C 메서드에 대해 두 번의 패스를 시작했습니다. 즉, "비공개"메소드를 완전히 선언하지 않고 호출 사이트의 위 또는 아래에 있는지 여부를 컴파일러가 찾을 수 있습니다.


37

나는 Objective-C 전문가는 아니지만 클래스 구현에서 메소드를 개인적으로 정의합니다. 물론, 그것을 호출하는 모든 메소드 전에 (위) 정의해야하지만, 최소한의 작업이 필요합니다.


4
이 솔루션은 컴파일러 경고를 피하기 위해 불필요한 프로그램 구조를 추가하지 않는 이점이 있습니다.
Jan Hettich

1
나는 이것을하는 경향이 있지만 Objective-C 전문가도 아닙니다. 전문가에게는 방법 주문 문제를 제외하고 이렇게하지 않는 이유가 있습니까?
Eric Smith

2
메소드 순서는 사소한 문제인 것 같지만 코드 가독성 으로 변환하면 특히 팀에서 작업 할 때 매우 중요한 문제가 될 수 있습니다.
borisdiakur

17
메소드 순서는 더 이상 중요하지 않습니다. LLVM의 최신 버전은 메소드가 어떤 순서로 구현되는지 상관하지 않습니다. 따라서 먼저 선언하지 않고도 주문에 자신을 맞출 수 있습니다.
Steve Waddicor

@justin
lagweezle의

19

@implementation블록 에서 개인 메소드를 정의하는 것이 대부분의 목적에 이상적입니다. Clang은 @implementation선언 순서에 관계 없이을 (를) 볼 수 있습니다 . 클래스 연속 (일명 클래스 확장) 또는 명명 된 범주에서 선언 할 필요는 없습니다.

경우에 따라 클래스 연속에서 메소드를 선언해야합니다 (예 : 클래스 연속과와 사이의 선택기를 사용하는 경우 @implementation).

static 기능은 특히 민감하거나 속도가 중요한 개인 방법에 매우 좋습니다.

접두사 이름 지정 규칙은 실수로 개인 메소드를 대체하지 않도록 도와줍니다 (클래스 이름을 접두어 안전으로 간주합니다).

명명 된 카테고리 (예 :) @interface MONObject (PrivateStuff)는로드시 잠재적 인 이름 충돌로 인해 특히 좋은 아이디어가 아닙니다. 친구 나 보호 방법에만 유용합니다 (아주 좋은 선택은 아닙니다). 불완전한 범주 구현에 대해 경고하려면 실제로 구현해야합니다.

@implementation MONObject (PrivateStuff)
...HERE...
@end

여기 주석이 달린 치트 시트가 있습니다 :

MONObject.h

@interface MONObject : NSObject

// public declaration required for clients' visibility/use.
@property (nonatomic, assign, readwrite) bool publicBool;

// public declaration required for clients' visibility/use.
- (void)publicMethod;

@end

MONObject.m

@interface MONObject ()
@property (nonatomic, assign, readwrite) bool privateBool;

// you can use a convention where the class name prefix is reserved
// for private methods this can reduce accidental overriding:
- (void)MONObject_privateMethod;

@end

// The potentially good thing about functions is that they are truly
// inaccessible; They may not be overridden, accidentally used,
// looked up via the objc runtime, and will often be eliminated from
// backtraces. Unlike methods, they can also be inlined. If unused
// (e.g. diagnostic omitted in release) or every use is inlined,
// they may be removed from the binary:
static void PrivateMethod(MONObject * pObject) {
    pObject.privateBool = true;
}

@implementation MONObject
{
    bool anIvar;
}

static void AnotherPrivateMethod(MONObject * pObject) {
    if (0 == pObject) {
        assert(0 && "invalid parameter");
        return;
    }

    // if declared in the @implementation scope, you *could* access the
    // private ivars directly (although you should rarely do this):
    pObject->anIvar = true;
}

- (void)publicMethod
{
    // declared below -- but clang can see its declaration in this
    // translation:
    [self privateMethod];
}

// no declaration required.
- (void)privateMethod
{
}

- (void)MONObject_privateMethod
{
}

@end

분명하지 않은 또 다른 접근법 : C ++ 유형은 매우 빠르며 훨씬 높은 수준의 제어를 제공하면서 내보내고로드 된 objc 메소드의 수를 최소화합니다.


1
메소드 이름 접두어로 전체 클래스 이름을 사용하는 경우 +1! 밑줄이나 자신의 TLA보다 훨씬 안전합니다. (비공개 메소드가 다른 프로젝트에서 사용하는 라이브러리에 있고 1 년 또는 2 년 전에 이미 이름을 사용한 것을 잊어 버린 경우 ...?)
big_m

14

인스턴스에 대한 포인터를 사용하는 구현 아래 또는 위에 정적 함수를 정의 할 수 있습니다. 모든 인스턴스 변수에 액세스 할 수 있습니다.

//.h file
@interface MyClass : Object
{
    int test;
}
- (void) someMethod: anArg;

@end


//.m file    
@implementation MyClass

static void somePrivateMethod (MyClass *myClass, id anArg)
{
    fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg);
}


- (void) someMethod: (id) anArg
{
    somePrivateMethod (self, anArg);
}

@end

1
Apple은 자체 용도로 밑줄이 붙은 이름을 예약했습니다.
Georg Schölly 2016 년

1
Apple의 프레임 워크를 사용하지 않으면 어떻게됩니까? 저는 애플의 프레임 워크없이 자주 Objective-C 코드를 개발합니다. 사실 Linux, Windows 및 Mac OS X에서 빌드합니다. Objective-C로 코딩하는 대부분의 사람들이 아마도 Mac OS X에서 코드를 사용한다는 점을 고려하여 어쨌든 제거했습니다.
dreamlax

3
나는 이것이 .m 파일의 진정한 개인 방법이라고 생각합니다. @interface ... @ end 블록에 메소드에 대해 private을 넣을 수 없기 때문에 다른 클래스 카테고리 메소드는 실제로 비공개가 아닙니다.
David.Chu.ca

왜 그렇게 하시겠습니까? 메소드 정의의 시작 부분에 단순히 "-"를 추가하면 매개 변수로 전달하지 않고 "self"에 액세스해야합니다.
Guy

1
@Guy :이 방법은 리플렉션에 의해 탐지 될 수 있기 때문에 전혀 사적인 것이 아닙니다.
dreamlax

3

블록을 사용할 수 있습니까?

@implementation MyClass

id (^createTheObject)() = ^(){ return [[NSObject alloc] init];};

NSInteger (^addEm)(NSInteger, NSInteger) =
^(NSInteger a, NSInteger b)
{
    return a + b;
};

//public methods, etc.

- (NSObject) thePublicOne
{
    return createTheObject();
}

@end

나는 이것이 오래된 질문이라는 것을 알고 있지만,이 질문에 대한 답을 찾을 때 처음 발견 한 것 중 하나입니다. 이 솔루션이 다른 곳에서 논의 된 것을 보지 못했기 때문에이 작업에 어리석은 것이 있으면 알려주십시오.


1
여기서 한 것은 함수보다 낫지 않은 전역 블록 유형 변수를 만드는 것입니다 (선언되지 않았기 때문에 실제로는 개인이 아닙니다 static). 그러나 나는 자바 스크립트 스타일 인 개인 ivar에 블록을 할당하는 실험을 해왔다. 이는 정적 기능으로는 불가능한 개인 ivar에 대한 액세스를 허용한다. 내가 어느 것을 선호하는지 아직 확실하지 않습니다.
big_m 2016

3

Objective C의 모든 객체는 performSelector : 메소드 를 보유하는 NSObject 프로토콜을 따릅니다 . 또한 이전에는 공개 수준에서 공개 할 필요가없는 "도우미 또는 개인"방법을 만드는 방법을 찾고있었습니다. 오버 헤드없이 개인 메소드를 만들고 헤더 파일에서 정의하지 않아도되는 경우이 샷을 제공하십시오 ...

아래 코드와 비슷한 서명으로 메소드를 정의하십시오 ...

-(void)myHelperMethod: (id) sender{
     // code here...
}

그런 다음 메소드를 참조해야 할 때 단순히 선택기로 호출하십시오 ...

[self performSelector:@selector(myHelperMethod:)];

이 코드 줄은 사용자가 만든 메소드를 호출하며 헤더 파일에 정의되지 않은 것에 대해 성가신 경고를하지 않습니다.


6
이런 식으로 세 번째 매개 변수를 전달할 방법이 없습니다.
Li Fumin

2

@interface맨 위 의 블록 을 피하려면 항상 비공개 선언을 MyClassPrivate.h이상적이지 않지만 구현을 방해 하지 않는 다른 파일에 넣을 수 있습니다.

MyClass.h

interface MyClass : NSObject {
 @private
  BOOL publicIvar_;
  BOOL privateIvar_;
}

@property (nonatomic, assign) BOOL publicIvar;
//any other public methods. etc
@end

MyClassPrivate.h

@interface MyClass ()

@property (nonatomic, assign) BOOL privateIvar;
//any other private methods etc.
@end

MyClass.m

#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass

@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;

@end

2

내가 여기서 보지 못한 것 하나 더-Xcode는 이름에 "_private"가있는 .h 파일을 지원합니다. MyClass 클래스가 있다고 가정 해 봅시다. MyClass.m 및 MyClass.h가 있고 이제 MyClass_private.h도 가질 수 있습니다. Xcode는이를 인식하여 Assistant Editor의 "Counterparts"목록에 포함시킵니다.

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"

1

문제 # 2를 해결할 방법이 없습니다. 이것이 바로 C 컴파일러 (따라서 Objective-C 컴파일러)가 작동하는 방식입니다. 당신은 엑스 코드 편집기를 사용하는 경우, 함수 팝업은 쉽게 탐색 할 수 있도록해야 @interface하고 @implementation파일에 블록을.


1

개인용 메소드가 없다는 이점이 있습니다. 숨기려는 논리를 별도의 클래스로 이동하여 위임으로 사용할 수 있습니다. 이 경우 대리자 개체를 개인으로 표시 할 수 있으며 외부에서 볼 수 없습니다. 로직을 별도의 클래스 (아마도 여러 개)로 옮기면 프로젝트를 더 잘 설계 할 수 있습니다. 클래스가 더 단순 해지고 메소드가 적절한 이름을 가진 클래스로 그룹화됩니다.


0

다른 사람들이 말했듯이 @implementation블록 에서 개인 메소드를 정의하는 것은 대부분의 목적으로 괜찮습니다.

코드 구성 주제 pragma mark private-Xcode에서 쉽게 탐색 할 수 있도록 코드를 함께 유지하고 싶습니다.

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

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