3

私はObjective-Cでランタイムプログラミングを行おうとしています。これを行うために、resolveClassMethodメソッドをオーバーライドします。

残念ながら、ARCがアクティブなときにclangでコンパイルエラーが発生します:

エラー:セレクター'動的'の既知のクラスメソッドがありません

エラーの代わりに警告を除いて、ARCなしでgccまたはclangを使用すると(-fno-objc-arcオプションが渡されます)、すべてが正常に機能します。

ARCは、戻り値を使用してメモリを管理する方法を理解するために呼び出されるメソッドの名前を知る必要があることを認識しています(メソッド名の規則に従います)。しかし、直接メソッド呼び出しの代わりに醜いperformSelector呼び出しなしでこの問題を解決するにはどうすればよいですか?

これが私のコードです:

Test.m

#import "Test.h"
#import <objc/runtime.h>

NSString* dynamicImp(id slef, SEL _cmd)
{
    NSLog(@"Dynamic method called");
    return @"dynamicImp";
}

@implementation Test

- (NSString*)name
{
    return @"John";
}

+ (BOOL)resolveClassMethod:(SEL)name
{
    if (name == @selector(dynamic))
    {
        Class metaClass = objc_getMetaClass([NSStringFromClass([self class]) UTF8String]);
        class_addMethod(metaClass, name, (IMP) dynamicImp, "@@:");
        return YES;
    }
    return NO;
}

+ (IMP)methodForSelector:(SEL)aSelector
{
    if (aSelector == @selector(dynamic))
    {
        return (IMP) dynamicImp;
    }
    else
    {
        return [super methodForSelector:aSelector];
    }
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if (aSelector == @selector(dynamic))
    {
        return YES;
    }
    else
    {
        return [NSObject respondsToSelector:aSelector];
    }
}

@end

Test.h

#import <Cocoa/Cocoa.h>

@interface Test : NSObject <NSObject> {
    NSString *_name;
}

- (NSString*)name;

@end

main.m

#import <Cocoa/Cocoa.h>
#import <stdio.h>
#import "Test.h"

int main(int argc, char* argv[])
{
    @autoreleasepool {
        Test *test = [[Test alloc] init];
        NSLog(@"Hello, %@", [test name]);
        NSLog(@"How are you , %@", [Test dynamic]);
    }
    return 0;
}

ARCなしのGccまたはclang

コンパイル結果

main.m:13:36:警告:クラスメソッド'+ dynamic'が見つかりません(戻り型のデフォルトは' id'です)

    NSLog(@"How are you , %@", [Test dynamic]);

出力

2012-10-22 10:33:15.563 test-clang [957:707]こんにちは、ジョン2012-10-22

2012-10-22 10:33:15.565 test-clang [957:707]2012-10-22と呼ばれる動的メソッド

2012-10-22 10:33:15.565 test-clang [957:707]お元気ですか、dynamicImp

ARCでClang

コンパイル結果

main.m:13:36:エラー:セレクター「動的」の既知のクラスメソッドがありません

    NSLog(@"How are you , %@", [Test dynamic]);

PS:私の目標は、ARCをアクティブにしてこのコードをコンパイルすることなので、メモリ管理については今のところ気にしませんでした。

4

2 に答える 2

3

あなたの電話で

NSLog(@"How are you , %@", [Test dynamic]);

ARCコンパイラはメソッドの戻り型を認識していません。ただし、ARCは、メソッドがオブジェクトを返し、ライフタイムを管理するための適切なretain/release呼び出しを追加するかどうかを知る必要があります。

ARCがなくても、コンパイラの警告が表示されます

クラスメソッド'+dynamic'が見つかりません(戻り型のデフォルトは' id'です)

しかし、ARCコンパイラはより厳密です。

あなたは呼び出すことができます

NSLog(@"How are you , %@", [[Test class] performSelector:@selector(dynamic)]);

performSelectorを返すためid。オブジェクト以外のものを返す関数には、を使用できますNSInvocation

dynamicまたは、クラス拡張を使用してメソッドを宣言することもできます。

@interface Test (DynamicMethods)
+ (NSString *)dynamic;
@end
于 2012-10-22T09:13:28.113 に答える
1

ARCは間違いなく、楽しいランタイムメソッド解決機構のいくつかにレンチを投入しました。ただし、まだいくつかのオプションがあります。あなたが言及したテクニックと同じくらい醜いのperformSelector:は、明示的なobjc_msgSend() 関数呼び出しです。関数は、次のように、戻り値と引数の型を指定してキャストする必要があります。

(void (*)(id, SEL)objc_msgSend)([Test class], @selector(dynamic)));

extern id objc_msgSend(id, SEL, ...);(暗黙の宣言に関する警告が表示されます。どこかで宣言するだけです。)

idより良いオプションは、メッセージを送信するときにオブジェクトをキャストすることです(またはid最初に変数に格納します)。コンパイラは、応答するメッセージについて何も知らidないため、任意のメッセージの送信について文句を言うことはできません。インスタンスと同じように、クラスオブジェクトをキャストidできます。

[(id)Test dynamic];

また

[(id)testInstance anotherDynamicName];
于 2012-10-22T19:44:48.047 に答える