속성이 포함 된 Objective-C 프로토콜을 처리하는 방법은 무엇입니까?


131

Objective-C 프로토콜의 사용법이 다음과 같은 방식으로 사용되는 것을 보았습니다.

@protocol MyProtocol <NSObject>

@required

@property (readonly) NSString *title;

@optional

- (void) someMethod;

@end

서브 클래스가 확장되는 구체적인 수퍼 클래스를 작성하는 대신이 형식이 사용되는 것을 보았습니다. 문제는이 프로토콜을 준수하면 속성을 직접 합성해야합니까? 수퍼 클래스를 확장하는 경우 대답은 '아니오'입니다. 그렇지 않아도됩니다. 그러나 프로토콜이 준수해야하는 속성은 어떻게 처리합니까?

이해하기 위해서는 여전히 이러한 속성이 필요한 프로토콜을 따르는 개체의 헤더 파일에서 인스턴스 변수를 선언해야합니다. 이 경우, 그것들이 단지 지침 원칙이라고 가정 할 수 있습니까? 필요한 방법의 경우도 마찬가지입니다. 프로토콜은 프로토콜이 나열하는 필수 방법을 제외하기 위해 손목을 때립니다. 속성 뒤에 숨겨진 이야기는 무엇입니까?

다음은 컴파일 오류를 생성하는 예입니다 (참고 : 현재 문제를 반영하지 않는 코드를 다듬 었습니다).

MyProtocol.h

@protocol MyProtocol <NSObject>

@required
@property (nonatomic, retain) id anObject;

@optional

TestProtocolsViewController.h

- (void)iDoCoolStuff;

@end

#import <MyProtocol.h>

@interface TestProtocolsViewController : UIViewController <MyProtocol> {

}

@end

TestProtocolsViewController.m

#import "TestProtocolsViewController.h"

@implementation TestProtocolsViewController
@synthesize anObject; // anObject doesn't exist, even though we conform to MyProtocol.

- (void)dealloc {
    [anObject release]; //anObject doesn't exist, even though we conform to MyProtocol.
    [super dealloc];
}

@end     

답변:


135

프로토콜은 단지 프로토콜을 통해 클래스에 대해 알고있는 모든 사람들에게 그 속성 anObject이있을 것이라고 말하고 있습니다. 프로토콜은 실제가 아니며 변수 나 메소드 자체가 없습니다. 프로토콜에 대한 참조를 보유한 객체가 특정 방식으로 사용할 수 있도록 클래스에 대한 특정 속성 세트 만 설명합니다.

그것은 당신의 클래스에서 당신의 프로토콜에 맞는 것을 의미하며, 당신은 anObject가 작동하도록 모든 것을해야합니다.

@property그리고 @synthesize코드를 생성하는 두 가지 메커니즘이 핵심입니다. @property해당 속성 이름에 getter (및 / 또는 setter) 메소드가 있다고 말하고 있습니다. 요즘 @property에는 시스템에 의해 생성 된 메소드와 저장 변수가 충분합니다 (추가해야했던 경우 @sythesize). 그러나 변수에 액세스하고 저장할 무언가가 있어야합니다.


80
프로토콜에 정의 된 속성의 경우 최신 런타임에서도 "@synthesize"가 필요하거나 인터페이스 정의에서 "@property"를 복제하여 자동 합성을 수행해야합니다.
Jeffrey Harris

@JeffreyHarris 스위프트에서 같은 것은 어떻습니까?
Karan Alangat

@KaranAlangat-Swift에는 \ @synthesize와 같은 것은 없지만 ObjC와 마찬가지로 프로토콜을 준수한다고 주장하는 클래스에서 속성을 선언해야합니다. Swift에서 함수의 기본 구현을 정의하는 카테고리를 만들 수 있지만 프로토콜의 기본 속성을 가질 수 없다는 것을 알 수 있습니다.
Kendall Helmstetter Gelner

31

다음은 프로토콜 정의가 완벽하게 작동하는 예제입니다.

@class ExampleClass;

@protocol ExampleProtocol

@required

// Properties
@property (nonatomic, retain) ExampleClass *item;

@end

다음은이 프로토콜을 지원하는 클래스의 실제 예입니다.

#import <UIKit/UIKit.h>
#import "Protocols.h"

@class ExampleClass;

@interface MyObject : NSObject <ExampleProtocol> {

    // Property backing store
    ExampleClass        *item;

}


@implementation MyObject

// Synthesize properties
@synthesize item;

@end

14

당신이 정말로해야 할 일은

@synthesize title;

당신의 구현에서 당신은 모두 설정되어야합니다. 클래스 인터페이스에 속성을 넣는 것과 같은 방식으로 작동합니다.

편집하다:

이 작업을보다 구체적으로 수행 할 수 있습니다.

@synthesize title = _title;

이것은 자동 합성을 사용하는 경우 xcode의 자동 합성이 속성과 ivar를 만드는 방법과 일치하므로 클래스에 프로토콜 및 클래스의 속성이있는 경우 일부 ivar의 형식이 다르므로 영향을 줄 수 있습니다 가독성.


1
당신은 완전히 확신합니까? 프로토콜에 선택적 속성이 설정되어 있으며 해당 프로토콜을 준수하는 구체적인 클래스에서만 @synthesize하면 선언되지 않은 변수라고 주장하는 컴파일러 오류가 발생합니다. 오타가 확인되지 않았습니다.
Coocoo4Cocoa

선택적 속성에 대해 잘 모르겠지만 mralex와 같이 언급하지 않은 한 가지 변수 제목의 이름을 지정하거나 @synthesize title = myinstancevar;
Kevlar

2
최신 런타임을 사용하는 경우 @synthesize 만 있으면 기본 ivar이 생성됩니다. 32 비트 x86을 대상으로하는 경우 레거시 런타임을 대상으로하기 때문에 컴파일러 오류가 언급됩니다.
Jeffrey Harris

1
자동 합성 은 Xcode 4.4에서 도입되었지만 Graham Lee트윗에 따르면 프로토콜에서 선언 된 속성은 다루지 않습니다. 따라서 여전히 해당 속성을 수동으로 합성해야합니다.
cbowns

이것은 좋은 지적이며, 추가하는 synthesize것이 충분 하다는 것을 알지 못했습니다 . 멋있는!
Dan Rosenstark

9

내 기사 PROTOCOL의 속성을 살펴보십시오

이름 속성을 선언하는 MyProtocol과이 프로토콜을 따르는 MyClass가 있다고 가정합니다.

주목할만한 것들

  1. MyClass의 identifier 속성은 getter, setter 및 backing _identifier 변수를 선언하고 생성합니다.
  2. name 속성은 MyClass에 헤더에 getter가 있음을 선언합니다. getter, setter 구현 및 지원 변수를 생성하지 않습니다.
  3. 이 이름 속성은 이미 프로토콜에 의해 선언되었으므로 다시 선언 할 수 없습니다. 오류가납니다

    @interface MyClass () // Class extension
    
    @property (nonatomic, strong) NSString *name;
    
    @end

프로토콜에서 속성을 사용하는 방법

이름 속성으로 MyClass를 사용하려면 다음 중 하나를 수행해야합니다.

  1. 속성을 다시 선언하십시오 (AppDelegate.h는 이런 식으로 수행합니다)

    @interface MyClass : NSObject <MyProtocol>
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, strong) NSString *identifier;
    
    @end
  2. 우리 자신을 종합하십시오

    @implementation MyClass
    
    @synthesize name;
    
    @end

목록 내에 중첩 된 코드 블록은 한 줄에 8 칸씩 들여 쓰기해야합니다. Markdown 구문은 비교적 알려지지 않았습니다. 귀하의 답변을 수정했습니다.
BoltClock

1

프로토콜 아키텍처

예 : 2 개의 클래스 (Person 및 Serial)는 Viewer의 서비스 사용을 원하며 ViewerProtocol을 준수해야합니다. viewerTypeOfDescription은 구독자 클래스가 준수해야하는 필수 특성입니다.

typedef enum ViewerTypeOfDescription {
    ViewerDataType_NSString,
    ViewerDataType_NSNumber,
} ViewerTypeOfDescription;

@protocol ViewerProtocol
@property ViewerTypeOfDescription viewerTypeOfDescription;
- (id)initConforming;
- (NSString*)nameOfClass;
- (id)dataRepresentation;
@end

@interface Viewer : NSObject
+ (void) printLargeDescription:(id <ViewerProtocol>)object;
@end

@implementation Viewer
+ (void) printLargeDescription:(id <ViewerProtocol>)object {
    NSString *data;
    NSString *type;
    switch ([object viewerTypeOfDescription]) {
        case ViewerDataType_NSString: {
            data=[object dataRepresentation];
            type=@"String";
            break;
        }
        case ViewerDataType_NSNumber: {
            data=[(NSNumber*)[object dataRepresentation] stringValue];
            type=@"Number";
            break;
        }
        default: {
            data=@"";
            type=@"Undefined";
            break;
        }
    }
    printf("%s [%s(%s)]\n",[data cStringUsingEncoding:NSUTF8StringEncoding],
           [[object nameOfClass] cStringUsingEncoding:NSUTF8StringEncoding],
           [type cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end


/* A Class Person */

@interface Person : NSObject <ViewerProtocol>
@property NSString *firstname;
@property NSString *lastname;
@end

@implementation Person
// >>
@synthesize viewerTypeOfDescription;
// <<
@synthesize firstname;
@synthesize lastname;
// >>
- (id)initConforming {
    if (self=[super init]) {
        viewerTypeOfDescription=ViewerDataType_NSString;
    }
    return self;
}
- (NSString*)nameOfClass {
    return [self className];
}
- (NSString*) dataRepresentation {
    if (firstname!=nil && lastname!=nil) {
        return [NSString stringWithFormat:@"%@ %@", firstname, lastname];
    } else if (firstname!=nil) {
        return [NSString stringWithFormat:@"%@", firstname];
    }
    return [NSString stringWithFormat:@"%@", lastname];
}
// <<
@end



/* A Class Serial */

@interface Serial : NSObject <ViewerProtocol>
@property NSInteger amount;
@property NSInteger factor;
@end

@implementation Serial
// >>
@synthesize viewerTypeOfDescription;
// <<
@synthesize amount;
@synthesize factor;
// >>
- (id)initConforming {
    if (self=[super init]) {
        amount=0; factor=0;
        viewerTypeOfDescription=ViewerDataType_NSNumber;
    }
    return self;
}
- (NSString*)nameOfClass {
    return [self className];
}
- (NSNumber*) dataRepresentation {
    if (factor==0) {
        return [NSNumber numberWithInteger:amount];
    } else if (amount==0) {
        return [NSNumber numberWithInteger:0];
    }
    return [NSNumber numberWithInteger:(factor*amount)];
}
// <<
@end




int main(int argc, const char * argv[])
{

    @autoreleasepool {

        Person *duncan=[[Person alloc]initConforming];
        duncan.firstname=@"Duncan";
        duncan.lastname=@"Smith";

        [Viewer printLargeDescription:duncan];

        Serial *x890tyu=[[Serial alloc]initConforming];
        x890tyu.amount=1564;

        [Viewer printLargeDescription:x890tyu];

        NSObject *anobject=[[NSObject alloc]init];

        //[Viewer printLargeDescription:anobject];
        //<< compilator claim an issue the object does not conform to protocol

    }
    return 0;
}

서브 클래 싱에 대한 프로토콜 상속을 가진 다른 예제

typedef enum {
    LogerDataType_null,
    LogerDataType_int,
    LogerDataType_string,
} LogerDataType;

@protocol LogerProtocol
@property size_t numberOfDataItems;
@property LogerDataType dataType;
@property void** data;
@end

@interface Loger : NSObject
+ (void) print:(id<LogerProtocol>)object;
@end

@implementation Loger
+ (void) print:(id<LogerProtocol>)object {
    if ([object numberOfDataItems]==0) return;
    void **data=[object data];
    for (size_t i=0; i<[object numberOfDataItems]; i++) {
        switch ([object dataType]) {
            case LogerDataType_int: {
                printf("%d\n",(int)data[i]);
            break;
            }
            case LogerDataType_string: {
                printf("%s\n",(char*)data[i]);
                break;
            }
            default:
            break;
        }
    }
}
@end


// A Master Class

@interface ArrayOfItems : NSObject  <LogerProtocol>
@end

@implementation ArrayOfItems
@synthesize dataType;
@synthesize numberOfDataItems;
@synthesize data;
- (id)init {
    if (self=[super init]) {
        dataType=LogerDataType_null;
        numberOfDataItems=0;
    }
    return self;
}
@end

// A SubClass

@interface ArrayOfInts : ArrayOfItems
@end

@implementation ArrayOfInts
- (id)init {
    if (self=[super init]) {
        self.dataType=LogerDataType_int;
    }
    return self;
}
@end

// An other SubClass

@interface ArrayOfStrings : ArrayOfItems
@end

@implementation ArrayOfStrings
- (id)init {
    if (self=[super init]) {
        self.dataType=LogerDataType_string;
    }
    return self;
}
@end


int main(int argc, const char * argv[])
{

    @autoreleasepool {

        ArrayOfInts *arr=[[ArrayOfInts alloc]init];
        arr.data=(void*[]){(int*)14,(int*)25,(int*)74};
        arr.numberOfDataItems=3;

        [Loger print:arr];

        ArrayOfStrings *arrstr=[[ArrayOfStrings alloc]init];
        arrstr.data=(void*[]){(char*)"string1",(char*)"string2"};
        arrstr.numberOfDataItems=2;

        [Loger print:arrstr];

    }
    return 0;
}

0

변수 anObject는 TestProtocolsViewController 클래스 정의에 정의되어야합니다. 프로토콜은 단지 거기에 있어야한다는 것을 알려주는 것입니다.

컴파일러 오류는 진실을 말해줍니다-변수가 존재하지 않습니다. @properties는 결국 도우미입니다.

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