2009-05-10 46 views
120

Tôi đã nhìn thấy việc sử dụng các giao thức Objective-C sẽ được sử dụng trong một thời trang như sau:Làm thế nào để xử lý các giao thức Objective-C có chứa các thuộc tính?

@protocol MyProtocol <NSObject> 

@required 

@property (readonly) NSString *title; 

@optional 

- (void) someMethod; 

@end 

Tôi đã nhìn thấy định dạng này sử dụng thay vì viết một lớp cha bê tông mà lớp con mở rộng. Câu hỏi đặt ra là, nếu bạn tuân theo 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. Nhưng làm thế nào để đối phó với các thuộc tính mà một giao thức yêu cầu phải tuân theo?

Để hiểu biết của tôi, bạn vẫn cần phải khai báo các biến cá thể trong tệp tiêu đề của một đối tượng tuân theo 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ể giả định rằng chúng chỉ là một nguyên tắc hướng dẫn? CLearly 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 yêu cầu mà một giao thức liệt kê. Câu chuyện đằng sau tài sản là gì?

Dưới đây là một ví dụ mà tạo ra một lỗi biên dịch (Lưu ý: Tôi đã tỉa mã mà không suy ngẫm về vấn đề trong tầm tay):

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  

Trả lời

125

Giao thức chỉ cho mọi người biết về lớp học của bạn thông qua giao thức, rằng thuộc tính anObject sẽ có ở đó. Giao thức không thực, chúng không có biến hoặc phương thức - chúng chỉ mô tả một tập hợ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 học 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@synthesize là hai cơ chế tạo mã cho bạn. @property chỉ là nói rằng sẽ có một phương thức getter (và/hoặc setter) cho tên thuộc tính đó. Những ngày này chỉ cần @property cũng đủ để có phương thức và biến lưu trữ được tạo cho bạn bởi hệ thống (bạn đã từ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ữ biến.

+72

Đố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. –

+0

@JeffreyHarris Còn về Swift thì sao? –

+0

@KaranAlangat - không có thứ gì như \ @ intnthesize trong Swift, nhưng cũng giống như ObjC bạn cần khai báo thuộc tính trong lớp yêu cầu tuân theo giao thức. Trong Swift, bạn có thể tạo một thể loại định nghĩa một thực thi mặc định của một hàm, nhưng theo như tôi đã có thể nói với bạn thì không thể có một thuộc tính mặc định cho một giao thức. –

13

tất cả các 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 sẽ có tất cả các thiết lập. nó hoạt động giống như cách đặt thuộc tính trong giao diện lớp của bạn.

Edit:

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

@synthesize title = _title; 

này sẽ rơi vào phù hợp với cách tổng hợp tự động xcode của tạo tài sản và ivars nếu bạn sử dụng tính năng tự động tổng hợp, vì vậy theo cách đó, 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ố các thanh ngang của bạn sẽ không có định dạng khác có thể ảnh hưởng đến 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 thiết lập trong một giao thức, và khi tôi chỉ @synthesize nó trong một lớp bê tông phù hợp với giao thức đó - tôi nhận được một lỗi trình biên dịch tuyên bố đó là một biến không khai báo. Không có lỗi chính tả nào được xác nhận. – Coocoo4Cocoa

+0

Tôi không chắc chắn về thuộc tính tùy chọn, nhưng một điều tôi quên đề cập đến như mralex nói là bạn cần phải buộc nó vào một biến thành viên, hoặc bằng cách đặt tên cho 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, những con ngà cơ bản sẽ được tạo ra cho bạn. Nếu bạn đang nhắm mục tiêu 32-bit x86, bạn sẽ nhận được lỗi trình biên dịch được đề cập, bởi vì bạn đang nhắm mục tiêu thời gian chạy kế thừa. –

0

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

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

27

Dưới đây là một ví dụ của tôi mà làm việc một cách hoàn hảo, định nghĩa giao thức đầu tiên của tất cả:

@class ExampleClass; 

@protocol ExampleProtocol 

@required 

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

@end 

Dưới đây là một ví dụ làm việc 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 
1

Protocol Architecture

Ví dụ: 2 lớp (Person và Serial) muốn sử dụng dịch vụ của Viewer ... và phải phù hợp với ViewerProtocol. viewerTypeOfDescription là lớp người đăng ký thuộc tính 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]; 
      [email protected]"String"; 
      break; 
     } 
     case ViewerDataType_NSNumber: { 
      data=[(NSNumber*)[object dataRepresentation] stringValue]; 
      [email protected]"Number"; 
      break; 
     } 
     default: { 
      [email protected]""; 
      [email protected]"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]; 
     [email protected]"Duncan"; 
     [email protected]"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 thí dụ khác với Nghị định thư thừa kế trên subclassing

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

Hãy xem bài viết của tôi PROPERTY IN PROTOCOL

Giả sử tôi có MyProtocol khai báo một property name, và MyClass rằng 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 tuyên bố và tạo ra getter, setter và ủng hộ _identifier biến tài sản
  2. Tên chỉ tuyên bố rằng MyClass có một getter, setter trong tiêu đề. Nó không tạo ra getter, setter implement và biến backup.
  3. Tôi không thể đăng ký lại thuộc tính tên này, vì nó đã được giao thức khai báo. Đừng này sẽ hét lên một lỗi

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

Làm thế nào để sử dụng tài sản trong giao thức

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

  1. Declare tài sản một lần nữa (AppDelegate.h làm 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 ourself

    @implementation MyClass 
    
    @synthesize name; 
    
    @end 
    
+0

Các khối mã lồng trong danh sách cần được thụt vào 8 dấu cách trên mỗi dòng. Đó là một sự kỳ quặc tương đối chưa biết 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

Thật không may, liên kết này không hoạt động. – Koen

Các vấn đề liên quan