8

Objective-Cで読み取り専用プロパティを読み取り書き込みプロパティに変える方法は何ですか? ソースにアクセスできないことに注意してください。

理由: 模擬目的で単体テストでこれを行う必要があります。

4

3 に答える 3

9

このようなプロパティの機能は、クラスのメイン実装ブロック、または少なくともそれを含むコンパイルユニットにアクセスせずに変更することはできません。これは、そのユニットの外部にあるivarにアクセスできないためです。カテゴリ内のクラスにセッターを追加したとしても、クラスの完全に外部からの場合よりも、クラスのストレージに影響を与えることはできません。

ただし、できることはKVCを使用することです。setValue:forKey:セッターをバイパスし、ivarが見つかった場合はivarに直接移動しますreadonly名前がわかっているバッキングストレージがあれば、これを使用して、宣言されたプロパティに対しても任意の値を設定できます。

こんなふうになります:

//Passaquisset.h
#import <Foundation/Foundation.h>

@interface Passaquisset : NSObject

@property (copy, readonly, nonatomic) NSString * vanadium;

@end

//Passaquisset.m
#import "Passaquisset.h"

@implementation Passaquisset

@synthesize vanadium;

- (id) init {

    self = [super init];
    if( !self ) return nil;

    vanadium = @"Number 23";

    return self;
}

@end

//Elsewhere...
#import <Foundation/Foundation.h>

#import "Passaquisset.h"

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

    @autoreleasepool {

        Passaquisset * pq = [Passaquisset new];
        NSLog(@"%@", [pq vanadium]);

        [pq setValue:@"Number 24" forKey:@"vanadium"];

        NSLog(@"%@", [pq vanadium]);

    }
    return 0;
}

_vanadium私が言ったように、プロパティの値が完全に計算されている場合など、同じ名前のセッターもivarも存在しない場合(またはアンダースコアが追加されている場合(KVCはかなりスマート))、これは失敗します(実際には例外が発生します)。

//Passaquisset.m

#import "Passaquisset.h"

@implementation Passaquisset

/** KVC will fail with this version! **/

- (NSString *)vanadium
{
    return @"Number 23";
}

@end

完全を期すために、プロパティが完全に異なる名前(たとえば)のivarによってサポートされている場合、KVCを使用するにはivarの@synthesize vanadium = erythronium;名前を知っている必要があることを述べておきます。

于 2013-01-27T19:48:12.133 に答える
3

セッター自体は合成されていないため、プロパティを単純にreadwrite変更してセッターにアクセスすることはできません。したがって、セッターはまったく存在しません。

あなたが考えているのは、ivar の名前を推測し、実行時にセッターを追加することです。

あなたのプロパティが呼び出されfoo、それがcopyプロパティを持っているとします。

  1. イヴァルの名前だと思います。で試してみましょう_foo

  2. セッターを用意する

    void fooSetter(id self, SEL _cmd, id newFoo) {
        Ivar ivar = class_getInstanceVariable(self, "_foo");
        id oldFoo = object_getIvar(self, ivar);
        if (oldFoo != newFoo)
             object_setIvar(self, ivar, [newFoo copy]);
    }
    
  3. resolveInstanceMethod:クラスメソッド内のクラスにセッターを追加する

    + (BOOL) resolveInstanceMethod:(SEL)aSEL {
        if (aSEL == @selector(setFoo:)) {
          class_addMethod(self, @selector(setFoo:), (IMP)fooSetter, "v@:@");
          return YES;
        }
        return [super resolveInstanceMethod:aSel];
    }      
    

この時点setFoo:で、実行時にメソッドをクラスに追加したので、次のようにしてアクセスできます。

YourClass yourObject = ...;
[yourObject setFoo:whatever];
于 2013-01-27T19:48:46.990 に答える
2

再度再宣言することを意味するプロパティをオーバーライドするとうまくいくと思います

.h ファイル内:

@property (readonly, copy) NSString *yourProperty;
In your .m file:

@interface MyClass ()

// Redeclare property as readwrite
@property (readwrite, copy) NSString *yourProperty;

@end


@implementation MyClass

@synthesize yourProperty;
@end

また

私はテストされていませんが、次のことを試してみる必要があると思います

[youReadOnlyrProperty retain]
于 2013-01-27T17:57:48.183 に答える