Làm thế nào để xử lý các giao thức Objective-C có chứa các thuộc tính?


131

Tôi đã thấy việc sử dụng các giao thức Objective-C được sử dụng theo kiểu như sau:

@protocol MyProtocol <NSObject>

@required

@property (readonly) NSString *title;

@optional

- (void) someMethod;

@end

Tôi đã thấy định dạng này được sử dụng thay vì viết một siêu lớp cụ thể mà các lớp con mở rộng. Câu hỏi là, nếu bạn tuân thủ giao thức này, bạn có cần tự tổng hợp các thuộc tính không? Nếu bạn đang mở rộng một siêu lớp, câu trả lời rõ ràng là không, bạn không cần phải làm vậy. Nhưng làm thế nào để một người đối phó với các thuộc tính mà một giao thức yêu cầu phải tuân thủ?

Theo hiểu biết của tôi, bạn vẫn cần khai báo các biến đối tượng trong tệp tiêu đề của một đối tượng phù hợp với một giao thức yêu cầu các thuộc tính này. Trong trường hợp đó, chúng ta có thể cho rằng chúng chỉ là một nguyên tắc chỉ đạo không? Hoàn toàn giống nhau không phải là trường hợp cho một phương pháp cần thiết. Trình biên dịch sẽ tát cổ tay của bạn để loại trừ một phương thức cần thiết mà một giao thức liệt kê. Câu chuyện đằng sau tài sản là gì?

Đây là một ví dụ tạo ra lỗi biên dịch (Lưu ý: Tôi đã cắt mã không phản ánh vấn đề trong tay):

MyProtocol.h

@protocol MyProtocol <NSObject>

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

@optional

TestProt ProtocolViewCont điều khiển.h

- (void)iDoCoolStuff;

@end

#import <MyProtocol.h>

@interface TestProtocolsViewController : UIViewController <MyProtocol> {

}

@end

TestProt ProtocolViewContoder.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     

Câu trả lời:


135

Giao thức chỉ nói cho mọi người biết về lớp của bạn thông qua giao thức, rằng thuộc tính anObjectsẽ ở đó. Các giao thức không có thật, chúng không có biến hoặc phương thức - chúng chỉ mô tả một tập các thuộc tính cụ thể đúng với lớp của bạn để các đối tượng giữ tham chiếu đến chúng có thể sử dụng chúng theo các cách cụ thể.

Điều đó có nghĩa là trong lớp của bạn phù hợp với giao thức của bạn, bạn phải làm mọi thứ để đảm bảo anObject hoạt động.

@property@synthesizelà trung tâm của hai cơ chế tạo mã cho bạn. @propertychỉ nói rằng sẽ có một phương thức getter (và / hoặc setter) cho tên thuộc tính đó. @propertyChỉ riêng những ngày này là đủ để có các phương thức và một biến lưu trữ được tạo bởi bạn bởi hệ thống (bạn thường phải thêm @sythesize). Nhưng bạn phải có một cái gì đó để truy cập và lưu trữ các biến.


80
Đối với các thuộc tính được xác định trong giao thức, bạn vẫn cần "@synthesize" ngay cả trong thời gian chạy hiện đại hoặc bạn cần sao chép "@property" trong định nghĩa giao diện của mình để tự động tổng hợp.
Jeffrey Harris

@JeffreyHarris Điều gì tương tự trong Swift ??
Karan Alangat

@KaranAlangat - không có thứ gọi là \ @synthesize trong Swift, nhưng giống như ObjC, bạn cần phải khai báo thuộc tính trong một lớp tuyên bố tuân thủ giao thức. Trong Swift, bạn có thể tạo một danh mục xác định cài đặt mặc định của hàm, nhưng theo như tôi đã có thể nói với bạn không thể có thuộc tính mặc định cho giao thức.
Kendall Helmstetter Gelner

31

Đây là một ví dụ của tôi hoạt động hoàn hảo, trước hết định nghĩa giao thức:

@class ExampleClass;

@protocol ExampleProtocol

@required

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

@end

Dưới đây là một ví dụ hoạt động của một lớp hỗ trợ giao thức này:

#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

tất cả những gì bạn phải làm thực sự là thả một

@synthesize title;

trong việc thực hiện của bạn và bạn nên được thiết lập tất cả. nó hoạt động tương tự như việc đặt thuộc tính vào giao diện lớp của bạn.

Biên tập:

Bạn có thể muốn làm điều này cụ thể hơn:

@synthesize title = _title;

Điều này sẽ phù hợp với cách tổng hợp tự động của xcode tạo ra các thuộc tính và ngà nếu bạn sử dụng tổng hợp tự động, do đó, nếu lớp của bạn có các thuộc tính từ một giao thức và một lớp, một số ngà của bạn sẽ không có định dạng khác nhau có thể ảnh hưởng khả năng đọc.


1
Bạn có hoàn toàn chắc chắn? Tôi có một thuộc tính tùy chọn được đặt trong một giao thức và khi tôi chỉ @synthesize nó trong một lớp cụ thể phù hợp với giao thức đó - tôi gặp lỗi trình biên dịch cho rằng đó là biến không được khai báo. Không có lỗi chính tả được xác nhận.
Coocoo4Cocoa

Tôi không chắc chắn về các thuộc tính tùy chọn, nhưng một điều tôi quên đề cập như mralex đã nói là bạn cần buộc nó vào một biến thành viên, bằng cách đặt tên tiêu đề biến đó, hoặc nói @synthesize title = myinstancevar;
Kevlar

2
Nếu bạn đang sử dụng thời gian chạy hiện đại, @synthesize là tất cả những gì bạn cần, các ngà bên dưới sẽ được tạo cho bạn. Nếu bạn đang nhắm mục tiêu x86 32 bit, bạn sẽ gặp lỗi trình biên dịch, vì bạn đang nhắm mục tiêu thời gian chạy cũ.
Jeffrey Harris

1
Tổng hợp tự động đã được giới thiệu trong Xcode 4.4, nhưng theo một tweet của Graham Lee , nó không bao gồm các thuộc tính được khai báo trong các giao thức. Vì vậy, bạn vẫn sẽ cần phải tự tổng hợp các thuộc tính đó.
cbowns

Đây là một điểm tuyệt vời, đã không nhận ra rằng thêm vào synthesizelà đủ. Mát mẻ!
Dan Rosenstark

9

Hãy xem bài viết của tôi TÀI SẢN TẠI PROTOCOL

Giả sử tôi có MyProtocol khai báo thuộc tính name và MyClass phù hợp với giao thức này

Những điều đáng lưu ý

  1. Thuộc tính định danh trong MyClass khai báo và tạo biến getter, setter và back _identifier
  2. Thuộc tính name chỉ tuyên bố rằng MyClass có getter, setter trong tiêu đề. Nó không tạo getter, cài đặt setter và biến sao lưu.
  3. Tôi không thể xác định lại thuộc tính tên này, vì nó đã được giao thức khai báo. Làm điều này sẽ hét một lỗi

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

Cách sử dụng thuộc tính trong giao thức

Vì vậy, để sử dụng MyClass với thuộc tính tên đó, chúng ta phải thực hiện một trong hai

  1. Khai báo lại tài sản (AppDelegate.h thực hiện theo cách này)

    @interface MyClass : NSObject <MyProtocol>
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, strong) NSString *identifier;
    
    @end
  2. Tổng hợp chính chúng ta

    @implementation MyClass
    
    @synthesize name;
    
    @end

Các khối mã được lồng trong danh sách cần được thụt lề bởi tám khoảng trắng trên mỗi dòng. Đó là một sự kỳ lạ tương đối lạ của cú pháp Markdown. Tôi đã chỉnh sửa câu trả lời của bạn cho bạn.
BoltClock

1

Kiến trúc giao thức

Ví dụ: 2 lớp (Người và Nối tiếp) muốn sử dụng dịch vụ của Trình xem ... và phải tuân theo ViewerProtocol. viewerTypeOfDes mô tả là một lớp thuê bao tài sản bắt buộc phải tuân thủ.

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;
}

Một ví dụ khác với kế thừa giao thức qua phân lớp

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

Biến, anObject, cần được định nghĩa trong định nghĩa lớp TestProt ProtocolViewControll của bạn, giao thức chỉ thông báo cho bạn rằng nó sẽ ở đó.

Các lỗi trình biên dịch đang cho bạn biết sự thật - biến không tồn tại. @properies chỉ là người giúp đỡ sau khi tất cả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.