ऑब्जेक्टिव-सी प्रोटोकॉल को कैसे संभालें जिसमें गुण हों?


131

मैंने देखा है कि ऑब्जेक्टिव-सी प्रोटोकॉल का उपयोग निम्न प्रकार से किया जाता है:

@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होगी। प्रोटोकॉल वास्तविक नहीं हैं, उनके पास स्वयं कोई चर या विधियां नहीं हैं - वे केवल उन विशिष्ट विशेषताओं का वर्णन करते हैं जो आपकी कक्षा के बारे में सच हैं ताकि उनके संदर्भों को रखने वाली वस्तुएं उन्हें विशिष्ट तरीकों से उपयोग कर सकें।

इसका मतलब है कि आपकी कक्षा में जो आपके प्रोटोकॉल के अनुरूप है, आपको यह सुनिश्चित करने के लिए सब कुछ करना होगा कि कौन सा काम करता है।

@propertyऔर @synthesizeदिल में दो तंत्र हैं जो आपके लिए कोड उत्पन्न करते हैं। @propertyकेवल यह कह रहा है कि उस संपत्ति के नाम के लिए एक गेट्टर (और / या सेटर) विधि होगी। इन दिनों @propertyअकेले भी तरीके और सिस्टम द्वारा आपके लिए बनाए गए स्टोरेज वेरिएबल के लिए पर्याप्त है (आपको जोड़ना पड़ता था @sythesize)। लेकिन आपके पास वैरिएबल को एक्सेस और स्टोर करने के लिए कुछ होना चाहिए।


80
किसी प्रोटोकॉल में परिभाषित गुणों के लिए, आपको अभी भी आधुनिक रनटाइम में "@ सिंथाइनसाइज़" की आवश्यकता है, या आपको ऑटो-सिंथेसिस प्राप्त करने के लिए अपने इंटरफ़ेस की परिभाषा में "@प्रॉपर्टी" की नकल करने की आवश्यकता है।
जेफरी हैरिस

@ जेफ्रीहेयर स्विफ्ट में उसी के बारे में क्या ??
करण अलंगट

@KaranAlangat - स्विफ्ट में \ @synthesize जैसी कोई चीज नहीं है, लेकिन ओबजैक की तरह आपको एक वर्ग में संपत्ति की घोषणा करने की आवश्यकता है जो प्रोटोकॉल के अनुरूप होने का दावा करती है। स्विफ्ट में आप एक श्रेणी बना सकते हैं जो फ़ंक्शन के डिफ़ॉल्ट कार्यान्वयन को परिभाषित करता है, लेकिन जहां तक ​​मैं आपको बता सकता हूं कि आपके पास प्रोटोकॉल के लिए डिफ़ॉल्ट संपत्ति नहीं है।
केंडल हेल्मसटेटर गेलनर

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 का स्वचालित संश्लेषण कैसे गुण और ivars बनाता है, इसके अनुरूप होगा, इस तरह यदि आपके वर्ग में प्रोटोकॉल और वर्ग से गुण हैं, तो आपके कुछ ivars का अलग स्वरूप नहीं होगा जो प्रभाव डाल सकता है पठनीयता।


1
क्या आपको पूरा यकीन है? मेरे पास एक प्रोटोकॉल में एक वैकल्पिक संपत्ति सेट है, और जब मैं केवल उस प्रोटोकॉल के अनुरूप एक ठोस वर्ग में @synthesize करता हूं - मुझे एक संकलक त्रुटि मिलती है जो यह दावा करती है कि यह एक अघोषित चर है। किसी भी टाइपो की पुष्टि नहीं हुई।
Coocoo4Cocoa

मैं वैकल्पिक गुणों के बारे में निश्चित नहीं हूं, लेकिन एक बात जिसका मैं उल्लेख करना भूल गया, जैसे कि mralex ने कहा है कि आपको इसे किसी सदस्य चर में बाँधने की जरूरत है, या तो उस चर शीर्षक को नाम देकर या @synthesize शीर्षक = myinstancevar;
केवलर

2
यदि आप आधुनिक समय का उपयोग कर रहे हैं, तो @ synthesize आपको केवल आवश्यकता है, आपके लिए अंतर्निहित ivars बनाए जाएंगे। यदि आप 32-बिट x86 को लक्षित कर रहे हैं, तो आपको संकलक त्रुटि का उल्लेख मिलेगा, क्योंकि आप विरासत रनटाइम को लक्षित कर रहे हैं।
जेफरी हैरिस

1
स्वचालित संश्लेषण Xcode 4.4 में पेश किया गया था, लेकिन ग्राहम ली के एक ट्वीट के अनुसार , यह प्रोटोकॉल में घोषित गुणों को कवर नहीं करता है। तो आपको अभी भी उन गुणों को मैन्युअल रूप से संश्लेषित करना होगा।
cbowns

यह एक महान बिंदु है, यह एहसास नहीं था कि जोड़ना synthesizeपर्याप्त था। ठंडा!
दान रोसेनस्टार्क

9

मेरे लेख PROTERTY IN PROTOCOL पर एक नज़र डालें

मान लीजिए कि मेरे पास MyProtocol है जो एक नाम संपत्ति की घोषणा करता है, और MyClass जो इस प्रोटोकॉल के अनुरूप है

ध्यान देने योग्य बातें

  1. MyClass में पहचानकर्ता संपत्ति घोषित करता है और गेटटर, सेटर और बैकिंग_एन्डीफायर चर उत्पन्न करता है
  2. नाम संपत्ति केवल घोषणा करती है कि MyClass के पास हेडर में एक गेट्टर, सेटर है। यह गेटटर, सेटर कार्यान्वयन और बैकिंग वैरिएबल उत्पन्न नहीं करता है।
  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

सूचियों के भीतर नेस्टेड कोड ब्लॉक को आठ स्थानों प्रति पंक्ति द्वारा इंडेंट करना होगा। यह मार्कडाउन सिंटैक्स की अपेक्षाकृत अज्ञात विषमता है। मैंने आपके लिए अपना उत्तर संपादित कर दिया है।
BoltClock

1

प्रोटोकॉल आर्किटेक्चर

उदाहरण: 2 वर्ग (व्यक्ति और सीरियल) दर्शक की सेवा का उपयोग करना चाहते हैं ... और ViewerProtocol के अनुरूप होना चाहिए। एक संभावित संपत्ति ग्राहक वर्गों के अनुरूप होना चाहिए।

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 सब के बाद मददगार हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.