Özellikler içeren Objective-C protokolleri nasıl kullanılır?


131

Objective-C protokollerinin aşağıdaki gibi kullanıldığını gördüm:

@protocol MyProtocol <NSObject>

@required

@property (readonly) NSString *title;

@optional

- (void) someMethod;

@end

Alt sınıfların genişletildiği somut bir üst sınıf yazmak yerine bu formatın kullanıldığını gördüm. Soru şu ki, bu protokole uyuyorsanız, özellikleri kendiniz sentezlemeniz gerekir mi? Bir süper sınıfı genişletiyorsanız, cevap kesinlikle hayır, buna gerek yok. Ancak bir protokolün uyması gereken özelliklerle nasıl başa çıkılır?

Anladığım kadarıyla, bu özellikleri gerektiren bir protokole uyan bir nesnenin başlık dosyasında örnek değişkenlerini bildirmeniz gerekiyor. Bu durumda, bunların sadece yol gösterici bir ilke olduğunu varsayabilir miyiz? C Hemen hemen aynı durum gerekli bir yöntem için geçerli değildir. Derleyici, bir protokolün listelediği gerekli bir yöntemi dışlamak için bileğinizi tokatlayacaktır. Mülklerin arkasındaki hikaye nedir?

İşte bir derleme hatası oluşturan bir örnek (Not: Eldeki sorunu yansıtmayan kodu kestim):

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     

Yanıtlar:


135

Protokol, protokol aracılığıyla sınıfınızı bilen herkese mülkün anObjectorada olacağını söylemektir. Protokoller gerçek değildir, değişkenleri veya metotları yoktur - yalnızca sınıfınız için geçerli olan belirli bir öznitelik kümesini açıklarlar, böylece onlara referanslar içeren nesneler bunları belirli şekillerde kullanabilir.

Bu, protokolünüze uyan sınıfınızda birObject'in çalıştığından emin olmak için her şeyi yapmanız gerektiği anlamına gelir.

@propertyve @synthesizeözünde sizin için kod üreten iki mekanizma vardır. @propertysadece bu özellik adı için bir alıcı (ve / veya ayarlayıcı) yöntemi olacağını söylüyor. @propertyYalnızca bu günler , sistem tarafından sizin için oluşturulmuş yöntemlere ve bir depolama değişkenine sahip olmak için yeterlidir (önceden eklemeniz gerekirdi @sythesize). Ancak değişkene erişmek ve depolamak için bir şeye sahip olmalısınız.


80
Bir protokolde tanımlanan özellikler için, modern çalışma zamanında bile bir "@ synthesize" gerekir veya otomatik sentez elde etmek için arayüz tanımınızdaki "@ özelliği" ni çoğaltmanız gerekir.
Jeffrey Harris

@JeffreyHarris Swift de aynı şey nedir?
Karan Alangat

@KaranAlangat - Swift'de \ @synthesize diye bir şey yoktur, ancak tıpkı ObjC gibi, protokole uygun olduğunu iddia eden bir sınıfta özelliği bildirmeniz gerekir. Swift'de bir işlevin varsayılan uygulamasını tanımlayan bir kategori oluşturabilirsiniz, ancak söyleyebildiğim kadarıyla bir protokol için varsayılan bir özelliğe sahip olamazsınız.
Kendall Helmstetter Gelner

31

İşte mükemmel çalışan bir örnek, her şeyden önce protokol tanımı:

@class ExampleClass;

@protocol ExampleProtocol

@required

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

@end

Aşağıda, bu protokolü destekleyen bir sınıfın çalışan bir örneği bulunmaktadır:

#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

gerçekten yapman gereken tek şey bir

@synthesize title;

uygulamanızda ve hazır olmanız gerekir. özelliği sınıf arayüzünüze koymakla aynı şekilde çalışır.

Düzenle:

Bunu daha spesifik yapmak isteyebilirsiniz:

@synthesize title = _title;

Bu, otomatik sentez kullanıyorsanız, xcode'un otomatik sentezinin özellikleri ve sarmalları nasıl oluşturduğuna uygun olacaktır, böylece sınıfınız bir protokol ve bir sınıftan özelliklere sahipse, ivarlarınızdan bazıları etkileyebilecek farklı biçime sahip olmayacaktır. okunabilirliği.


1
Tamamen emin misin? Bir protokolde ayarlı isteğe bağlı bir özellik var ve bunu yalnızca bu protokole uygun somut bir sınıfta @ sentezlediğimde - bunun bildirilmemiş bir değişken olduğunu iddia eden bir derleyici hatası alıyorum. Yazım hatası onaylanmadı.
Coocoo4Cocoa

isteğe bağlı özellikler hakkında emin değilim, ancak mralex'in dediği gibi bahsetmeyi unuttuğum bir şey, onu bir üye değişkene bağlamanız gerektiğidir, bu değişken başlığını adlandırarak veya @ synthesize title = myinstancevar diyerek;
Kevlar

2
Modern çalışma süresini kullanıyorsanız, ihtiyacınız olan tek şey @synthesize'dır, temeldeki ivarlar sizin için oluşturulacaktır. 32 bit x86'yı hedefliyorsanız, bahsedilen derleyici hatasını alırsınız çünkü eski çalışma zamanını hedefliyorsunuz.
Jeffrey Harris

1
Otomatik sentez , Xcode 4.4'te tanıtıldı, ancak Graham Lee'nin bir tweet'ine göre , protokollerde bildirilen özellikleri kapsamıyor. Bu nedenle, bu özellikleri manuel olarak sentezlemeniz gerekecek.
cbowns

Bu harika bir nokta, eklemenin synthesizeyeterli olduğunu anlamadım . Güzel!
Dan Rosenstark

9

PROPERTY IN PROTOCOL adlı makaleme bir göz atın

Bir isim özelliği bildiren MyProtocol ve bu protokole uyan MyClass olduğunu varsayalım

Kaydedilmeye değer şeyler

  1. MyClass'daki identifier özelliği, alıcı, ayarlayıcı ve destekleyici _identifier değişkenini bildirir ve oluşturur
  2. Name özelliği yalnızca MyClass'ın başlıkta bir alıcı ve ayarlayıcıya sahip olduğunu bildirir. Alıcı, ayarlayıcı uygulaması ve destek değişkeni oluşturmaz.
  3. Protokol tarafından zaten bildirildiği için bu ad özelliğini yeniden beyan edemiyorum. Bunu yapmak bir hata verecektir

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

Protokolde özellik nasıl kullanılır

Yani MyClass'ı bu isim özelliği ile kullanmak için ikisinden birini yapmalıyız

  1. Özelliği tekrar bildirin (AppDelegate.h bu şekilde yapar)

    @interface MyClass : NSObject <MyProtocol>
    
    @property (nonatomic, strong) NSString *name;
    
    @property (nonatomic, strong) NSString *identifier;
    
    @end
  2. Kendimizi sentezle

    @implementation MyClass
    
    @synthesize name;
    
    @end

Listelerin içine yerleştirilmiş kod bloklarının her satırda sekiz boşlukla girintilendirilmesi gerekir. Markdown sözdiziminin nispeten bilinmeyen bir tuhaflığı. Cevabınızı sizin için düzenledim.
BoltClock

1

Protokol Mimarisi

Örnek: 2 sınıf (Kişi ve Seri) Viewer hizmetinin kullanılmasını ister ... ve ViewerProtocol'a uymalıdır. viewerTypeOfDescription, abone sınıflarının uyması gereken zorunlu bir özelliktir.

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

Alt sınıflandırma üzerinden Protokol kalıtımına sahip başka bir Örnek

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 değişkeninin TestProtocolsViewController sınıf tanımınızda tanımlanması gerekir, protokol sadece orada olması gerektiğini size bildirir.

Derleyici hataları size doğruyu söylüyor - değişken mevcut değil. @ mülkler sonuçta sadece yardımcılardır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.