13

更新 - 多くの人が、プロパティの iVar を宣言する必要があると主張しています。私は最新のランタイム (64 ビット) を使用しているため、そうではないという人もいます。@property を iVar なしで何ヶ月も正常に使用していることを確認できます。したがって、「正しい」答えは、64ビットで、子クラスからアクセスするときに(そしてそのときにのみ)iVarを明示的に宣言する必要がある理由についての説明だと思います。これまでに見た唯一のバグは、GCC のバグの可能性です (Yuji さんに感謝します)。結局のところ、それほど単純ではありません...考えられるバグを明確にするために、次のようにします。基本クラスから継承する場合、子は親の iVar IF にアクセスできません。その子は、iVar にアクセスする前に @synthesize を使用して UNRELATED アクセサーを実装することもあります。

私はこれで数時間頭を悩ませてきました - 私は継承をあまり使っていません。

ここでは、ivar が宣言されているテスト A から継承する単純なテスト B クラスをセットアップしました。しかし、変数が宣言されていないというコンパイルエラーが発生します。これは、プロパティを追加して宣言を合成した場合にのみ発生します - それらがなくても正常に動作します。

TestA ヘッダー:

#import <Cocoa/Cocoa.h>
@interface TestA : NSObject {
    NSString *testString;
}
@end

TestA 実装は空です:

#import "TestA.h"
@implementation TestA  
@end

TestB ヘッダー:

#import <Cocoa/Cocoa.h>
#import "TestA.h"
@interface TestB : TestA {
}
@property (nonatomic, retain) NSString *testProp;
@end

TestB の実装 (エラー - 'testString' が宣言されていません)

#import "TestB.h"
@implementation TestB
@synthesize testProp;
- (void)testing{
    NSLog(@"test ivar is %@", testString);
}
@end
4

4 に答える 4

10

これは GCC 4.2.1 のバグだと思います。foo.m内容でファイルを作りました

#import <Foundation/Foundation.h>
@interface TestA : NSObject {
    NSString *testString;
}
@end

@implementation TestA  
@end

@interface TestB : TestA {
}
@property (retain) NSString *testProp;
@end

@implementation TestB
@synthesize testProp;
- (void)testing{
NSLog(@"test ivar is %@", testString);
}
@end

64 ビット モードでは、インスタンス変数を省略しても問題ないことに注意してください。OS X 10.6.3 上の GCC 4.2.1 でエラーが発生しました。

$ gcc -arch x86_64 -c foo.m
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)

これは、変更することで問題なくコンパイルされました

NSLog(@"test ivar is %@", testString);

NSLog(@"test ivar is %@", self->testString);

Clangは問題なくコンパイルしました。

(32ビットモードでは、

$ gcc -arch i386 -c foo.m
aho.m:17: error: synthesized property ‘testProp’ must either be named 
the same as a compatible ivar or must explicitly name an ivar
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)

Manjunath が書いたように、これは完全に予想される動作です。)

ただし、スーパークラスのインスタンス変数にアクセスするのは一般的にかなり悪い考えだと思います。スーパークラスのメソッドを実装する場合、サブクラスによって可能な限り最悪の方法で調整される可能性があるため、インスタンス変数について何も想定できません。少なくとも、インスタンス変数で許可されている操作と許可されていない操作の種類を書き留める必要があります...コードを何年も維持する必要があるかもしれないことを忘れないでください! メソッドとプロパティのレベルで、コードのさまざまな部分の間でプログラミング契約を維持することをお勧めします。

最後に変更する必要があります

@property NSString *testProp;

@property (copy) NSString *testProp;

または少なくとも

@property (retain) NSString *testProp;

OS X で GC を使用していない場合。

于 2010-05-06T04:54:58.757 に答える
1

同じ問題に遭遇しましたが、ソースが複雑すぎて、GCC を使用したときに親からの iVar にアクセスできなくなった理由がよくわかりませんでした。数か月前とコードを変更する前に、それが機能し、しばらく使用していたclangで機能していることを確信していました。突然、GCC でビルドする必要がありましたが、それはできなくなりました。

少なくとも、この記事で回避できました (iVar を宣言してください)。XCode の最新バージョンに修正済みのコンパイラが含まれていないことに驚いています

于 2011-03-30T20:44:50.353 に答える
1

タイプミスがあるだけだと思います-「test」ではなく「testString」にする必要があります

于 2010-05-06T04:17:02.757 に答える
1

がメソッドの直前にあるerror: 'testString' undeclared (first use in this function)のを見ています。メソッドの実装の下に移動すると、エラーは消えます。これは、TestB クラスに、宣言されたプロパティで使用する文字列インスタンス変数がないことが原因である可能性があります。(レガシー (32 ビット) ランタイムでは、プロパティに使用するインスタンス変数を宣言する必要があります。モダン ランタイム (64 ビット Mac、iPhone) では、推論できるため、宣言はオプションです。)代わりにプロパティに名前を付けるには?@synthesizetesting@synthesizetestProptestString


編集: GCC 4.2 では、TestB.h を次のように変更すると機能します。

#import "TestA.h"

@interface TestB : TestA {
    NSString *testProp; // <-- Adding this fixes the errors
}
@property NSString *testProp;

@end

ただし、Clang-LLVM コンパイラを使用すると、コードは変更されずに機能します。おそらく、これはファイルするバグです。

于 2010-05-06T06:00:58.447 に答える