5

「フォーマル」を強制しようとして@protocolいますが、クラス/インスタンスがプロトコルの「必要な」メソッドを実際に実装しているかどうか、またはプロトコルに準拠していることを単に「宣言」しているかどうかについて、クラス/インスタンスを確実にテストできません。

私の困惑の完全な例…

#import <Foundation/Foundation.h>

@protocol       RequiredProtocol 
@required                   
- (NSString*) mustImplement;                              @end 
@interface      Cog         : NSObject <RequiredProtocol> @end
@implementation Cog                                       @end
@interface      Sprocket    : NSObject                    @end 
@implementation Sprocket 
- (NSString*) mustImplement
  { return @"I conform, but ObjC doesn't care!"; }        @end

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

    Protocol *required = @protocol(RequiredProtocol);
    SEL    requiredSEL = @selector(mustImplement);
    void (^testProtocolConformance)(NSObject*) = ^(NSObject *x){
        NSLog(@"Protocol:%@\n"
               "Does %@ class conform:%@     \n"
               "Do  instances conform:%@     \n"
               "Required method's result:\"%@\"", 
        NSStringFromProtocol ( required ),
        NSStringFromClass    ( x.class  ), 
        [x.class conformsToProtocol:required] ? @"YES" : @"NO", 
        [x       conformsToProtocol:required] ? @"YES" : @"NO",
        [x    respondsToSelector:requiredSEL] ? [x mustImplement]
                                              : nil );
    };
    testProtocolConformance ( Cog.new      );
    testProtocolConformance ( Sprocket.new );
}

結果:

Protocol:RequiredProtocol
Does Cog class conform:YES
Do instances conform:YES
Required method's result:"(null)"

Protocol:RequiredProtocol
Does Sprocket class conform:NO
Do instances conform:NO
Required method's result:"I conform, but ObjC doesn't care!"

@protocolのメソッド ( ) を実装するクラスとそのインスタンスが にSprocket戻るのNOconformsToProtocolなぜですか?

そして、実際には準拠していないのに、準拠していると言っているものが ( Cog) を返すのはYESなぜですか?

適合性を装うために必要なのは宣言だけである場合、正式なプロトコルのポイントは何ですか?

@selector複数の を呼び出すことなく、複数の の完全な実装を実際に確認するにはどうすればよいですrespondsToSelectorか?

@Josh Caswell .. diff2 つを ing せずに.. あなたの応答は、NSObject私がその間に使用していたカテゴリと同様の効果を達成すると思います…</p>

@implementation NSObject (ProtocolConformance)
- (BOOL) implementsProtocol:(id)nameOrProtocol {
   Protocol *p = [nameOrProtocol isKindOfClass:NSString.class] 
               ? NSProtocolFromString(nameOrProtocol) 
               : nameOrProtocol;  // Arg is string OR protocol
   Class klass = self.class;
   unsigned int outCount = 0;
   struct objc_method_description *methods = NULL;
   methods = protocol_copyMethodDescriptionList( p, YES, YES, &outCount);
   for (unsigned int i = 0; i < outCount; ++i) {
       SEL selector = methods[i].name;
       if (![klass instancesRespondToSelector: selector]) {
           if (methods) free(methods); methods = NULL; return NO;
       }
    }
    if (methods) free(methods); methods = NULL; return YES;
}
@end
4

4 に答える 4

2

CRDは正しいです。コンパイラは実際の適合性について教えてくれるので、それを聞く必要があります。それが無視されている場合、ランタイムには再確認するための組み込みメソッドがありません。クラスは、プロトコル オブジェクトの内部リストを内部的に維持します。conformsToProtocol:それを見ているだけです。

誰かがやって来て、 #@(%!^& ランタイムをもう一度いじるのをやめろと言ってくるかもしれませんが、実際の実装を本当に本当にチェックする必要がある場合は、これができる 1 つの方法です。

#import <objc/runtime.h>

BOOL classReallyTrulyDoesImplementAllTheRequiredMethodsOfThisProtocol(Class cls, Protocol * prtcl)
{
    unsigned int meth_count;
    struct objc_method_description * meth_list;
    meth_list = protocol_copyMethodDescriptionList(p, 
                                                   YES /*isRequired*/,
                                                   YES /*isInstanceMethod*/,
                                                   &meth_count);
    /* Check instance methods */ 
    for(int i = 0; i < meth_count; i++ ){
        SEL methName = meth_list[i].name;
        if( ![class instancesRespondToSelector:methName] ){
            /* Missing _any_ required methods means failure */ 
            free(meth_list);
            return NO;
        }
    }
    free(meth_list);

    meth_list = protocol_copyMethodDescriptionList(p, 
                                                   YES /*isRequired*/,
                                                   NO /*isInstanceMethod*/,
                                                   &meth_count);
    /* Check class methods, if any */
    for(int i = 0; i < meth_count; i++ ){
        SEL methName = meth_list[i].name;
        if( ![class respondsToSelector:methName] ){
            free(meth_list);
            return NO;
        }
    }
    free(meth_list);                          

    return YES;
}

ハンマーがあれば…

于 2013-05-29T19:13:06.813 に答える
0

これらの答えはすべて良いです。彼らに、もう 1 点付け加えておきますconformsToProtocol:。ほとんどの場合、電話をかけるのは間違いです。クラスが実際に特定のメソッドを提供しているかどうかではなく、クラスがプロトコルに準拠していると言っているかどうかを示しているためです。

  • 準拠していると主張するが準拠していないクラスを作成することは可能です。さまざまな警告を黙らせて、必要なメソッドが存在すると仮定すると、クラッシュが発生します。
  • プロトコルに準拠しているが準拠しているとは主張しないクラスを作成することは可能であり、その結果、メソッドが存在していても、デリゲートでメソッドが呼び出されなくなります。
  • プロトコルが変更されると、プログラミング エラーが忍び寄る可能性があります。これは、以前は必要でしたが、もはや必要ではないメソッドを呼び出す前に、コードがプロトコルへの準拠をチェックするためです。

これらの問題はすべて、予期しない動作を引き起こす可能性があります。

IMO、クラスがメソッドを処理するかどうかを知りたい場合、最も安全な方法は、respondsToSelector:たまたまそのメソッドを含むプロトコルに準拠しているかどうかを尋ねるのではなく、そのメソッドを処理するかどうかを明示的に尋ねることです ( )。

IMOは、conformsToProtocol:NSObjectで公開されるのではなく、実際にはObjective-Cランタイムの関数であるべきでした。これは、一般に、解決するよりも多くの問題を引き起こすためです。

于 2018-06-06T19:59:42.070 に答える