9

UIView サブクラスには、次のプロパティがあります。

@property (nonatomic) CGFloat scale;

#define DEFAULT_SCALE 0.90

そして、このゲッターとセッター:

-(CGFloat)scale
{
    if (!self.scale) {
        return DEFAULT_SCALE;
    }else{
        return self.scale;
    }
}

-(void)setScale:(CGFloat)scale
{
    if (scale != self.scale) {
        self.scale = scale;
        [self setNeedsDisplay];
    }

}

たとえば、getter で self.scale をチェックすると無限ループが発生するため、これは正しくありません。無限ループに陥らないようにゲッターとセッターを記述する適切な方法は何ですか?

4

3 に答える 3

21

としてivarに直接アクセスできるはずです_scale。ゲッター/セッターは次のようになります。

更新:@ wattson12が以下のコメントで指摘しているよう@synthesizeに、実装にを追加する必要があります。

@synthesize scale = _scale;

-(CGFloat)scale
{
    if (!_scale) {
        return DEFAULT_SCALE;
    }else{
        return _scale;
    }
}

-(void)setScale:(CGFloat)scale
{
    if (scale != _scale) {
        _scale = scale;
        [self setNeedsDisplay];
    }

}
于 2012-10-31T16:36:53.790 に答える
2

ドット表記は、最初は少し誤解を招く可能性があります。

セッター

あなたがここに投稿した行で

self.scale = scale;

ローカル変数に割り当てていません。実際、メッセージ-setScale:をに送信していますself。この行は、

[self setScale:scale];

-setScale:内から呼び出しているので-setScale:、この無限の再帰が発生します。

あなたがする必要があるのは、(それ自体の中からあなたのセッターを呼び出すのではなく)あなたのセッターにインスタンス変数を設定することです。通常、書くだけで

@property (nonatomic) CGFloat scale;

インスタンス変数を作成しました_scaleただし、との両方-scaleを上書きしたため-setScale:、このインスタンス変数は作成されません。したがって、インスタンス変数を自分で追加する必要があります。クラスの宣言@interface内(または、クラス拡張 @interface内)

//If adding the instance variable to the class declaration:
@interface MyClass : Superclass
{
    //....
    CGFloat _scale;
}
//....
@end

これを行った後、行をに変更するだけで十分です

_scale = scale;

ゲッター

あなたが投稿した他の2つの問題のある行があります。これらはゲッターにあります。最初は

return self.scale;

- (CGFloat)scale。以前と同様に、このドット表記は、あなたがそれが意味すると思うかもしれないことを意味するものではありません。実際、それは

return [self scale];

以前のように、これは無限の再帰を引き起こしています。2番目は

if (!self.scale) {

同じ理由で問題がありself.scaleます。評価されたときの式は[自己スケール]です。繰り返しますが、これは無限の再帰を引き起こします。これらの両方の修正は、このゲッターself.scaleを残すことに置き換えることです。_scale

- (CGFloat)scale
{
    if (!_scale) {//Since CGFloat is not an object, this means <<if (_scale == 0) {>>
        return DEFAULT_SCALE;
    } else {
        return _scale
    }
}

より良い方法

ここでは、本来あるべきよりもはるかに多くの仕事をしています。イニシャライザを利用する方がはるかに良いでしょう:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    {
        self.scale = DEFAULT_SCALE;
    }
}

scaleこれにより、が設定されていない場合は、が返されることが保証されDEFAULT_SCALEます。これにより、ゲッター(およびその結果として)を完全に排除できます@synthesize。セッターで呼び出しているので-setNeedsDisplay、それでも必要になります。

- (void)setScale:(CGFloat)scale
{
    if (_scale != scale) {
        _scale = scale;
        [self setNeedsDisplay];
    }
}
于 2012-10-31T16:39:19.193 に答える
2

私はこれを行うための約 3 つの方法を考えることができます。

@interface someClass
{
    BOOL useCustomScale;
}
@property float scale;
@end
@implimentation someClass
-(float)scale
{
    if(useCustomScale)
    {return scale;}
    return defaultScale;
}
-(void) setScale: (float)someScale
{
    useCustomScale = YES;
    scale = someScale
}

それ以外の場合は、スケール値をバックアップするために NSNumber を使用できます...
そうでない場合は、スケールを -1 に初期化し、セッターでそれを不正な値にすることができます。
0 を有効な値にしたい場合があるため、通常、テストに 0 を使用するのは非常に悪いことです。

于 2012-10-31T17:01:59.957 に答える