0

私は可変性をいじっていましたが、不変オブジェクトを可変オブジェクトにキャストできる次のコードを思いつきました。

- (NSString *) getaString {
    NSMutableString * string = [[NSMutableString alloc] init];
    [string appendString:@"This "];
    [string appendString:@"was mutable."];
    return string;                          
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //Get a string returns an NSString, which is then cast to a mutable string with a compler warning.
    NSMutableString * string = [self getaString];
    [string appendString:@" And apparently still is"];
    _showText.text = string;
}

またはコンパイラの警告なし

- (NSArray *) getaString {
    NSMutableString * string = [[NSMutableString alloc] init];
    [string appendString:@"This "];
    [string appendString:@"was mutable."];
    //Cast to NSString.
    NSString * immutableString = string;
    NSArray * array = [[NSArray alloc] initWithObjects:immutableString, nil];
    return array;                          
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableString * string = [[self getaString] objectAtIndex:0];
    [string appendString:@" And apparently still is"];
    _showText.text = string;
}

UITextField は、コンパイラの警告なしで、「これは変更可能でした。そして、どうやらまだそうです」という文字列全体を示しています。単純に可変オブジェクトを不変オブジェクトとしてキャストまたは使用することを推奨する複数の SO 投稿を見てきましたが、先ほど示したように、それは危険な場合があります。また、配列がなくてもキャストは機能しますが、コンパイラの警告が表示されます。

私の質問は、Java スタイルの防御コピーの使用を検討すべきですか? Objective C で防御的コピーについての言及を見たことがなく、Apple のドキュメントで見つけることができたのは、いくつかの防御的プログラミング手法を採用するのが最善であるという漠然とした言及だけでした。セキュリティと不注意なコーディングからの保護の両方に関心があります。

4

3 に答える 3

4

あなたが示した例は誤解を招くものです。

配列に a を格納してNSMutableStringいて、それを取り出しています。その後、文字列が不変であると期待するのはなぜですか?

ただし、Objective-C では防御コピーがほぼ標準になっている場合があります。

CustomClassのプロパティとNSStringそれを出力するメソッドを定義するクラス ( と呼びましょう) を考えてみましょう。

@property (nonatomic, strong) NSString *aString;
- (void)printTheString;

NSMutableStringは のサブクラスであるためNSString、このクラスのクライアントは次のようなことを実行できる可能性があります。

CustomClass *anObject = [CustomClass new];
NSMutableString *aMutableString = [NSMutableString stringWithString:@"Hey!"];
anObject.aString = aMutableString;
[anObject printTheString]; // Hey!
[aMutableString appendString:@" Got you!"];
[anObject printTheString]; // Hey! Got you!

場合によっては危険です。

その後、可変サブクラスを持つ不変クラスには、 のcopy代わりに属性を使用することが一般的な方法になりました。strong

@property (nonatomic, copy) NSString *aString;

このようにして、文字列が割り当てられたときに防御的なコピーが作成され、クライアントが後でオブジェクトをいじるのを防ぎます。

前の例では、Hey!両方の時間が出力されます。

copyこのクラスのほとんどで、不変オブジェクトに送信すると、実際のコピーではなく、同じオブジェクトが返されることにも注意してください。このようにして、コピーは必要な場合にのみ実行されるため、ケーキを持って食べることもできます。

于 2013-11-04T04:02:24.867 に答える
0

NSStringから不変を取得しようとしている場合はNSMutableString、次のようにすることができます。

NSString *anImmutableString = [NSString stringWithString: aMutableString];

したがって、質問で元のコードを取り上げるとviewDidLoad、不変の文字列が必要な場所にあると仮定します。

- (NSArray *) getaString {
    NSMutableString * string = [[NSMutableString alloc] init];
    [string appendString:@"This "];
    [string appendString:@"was mutable."];
    NSArray * array = [[NSArray alloc] initWithObjects:string, nil];
    return array;                          
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *string = [NSString stringWithString:[[self getaString] objectAtIndex:0]];
    //[string appendString:@" And apparently still is"];
    _showText.text = string;
}

stringそれでもinに追加したい場合viewDidLoadは可能ですが、別の方法で行う必要があります (他の に追加するのと同じ方法NSString)。次のようになります。

NSString *newString = [string stringByAppendingString:@" And apparently still is"];

次に、変数の内容を見てください。

string = "This was mutable." //as you built it from an NSMutableString in the getaString method
newString = "This was mutable. And apparently still is" //as built from calling "stringByAppendingString" method on your variable string in viewDidLoad

newStringここでは、 の内容がかなり誤解を招く ことに注意することが重要です。newStringではありませんNSMutableString。レギュラーNSStringです。を呼び出すと、メソッドを呼び出している の末尾に送信した引数を追加した結果である がstringByAppendingString返されます。(そして、メソッドを呼び出した内容は変更されません。つまり、不変です)。NSStringNSStringNSString

になりたい場合は、次のようnewStringにするNSMutableString必要があります。

NSMutableString *newString = [[string stringByAppendingString:" And apparently still is"] mutableCopy];
于 2013-11-04T15:43:14.300 に答える
0

Obj-c で防御的なコピーを作成する必要があるかどうかはわかりませんが、不変文字列の可変コピーを作成することは可能であり、可変文字列の不変バージョンを作成することは可能です。

ところで、あなたのコードには不変の文字列はありません。NSArray 内の NSMutableStrings は引き続き可変であるため、変換は行われません。配列は、オブジェクトを追加できないものです。

理由は次のとおりです。配列は、実際のオブジェクトではなく、NSMutableString へのポインターを保持しているだけです。したがって、配列が指すメモリ アドレスは同じままであるため、文字列の内容を変更することによって配列の内容を変更することはありません。 .

例:

NSString *original = @"Hello, world!";

// putting the original in a mutable string
NSMutableString *copy = [original mutableCopy];

NSArray *array = [NSArray arrayWithObjects:copy, nil];

// the object in the array can still be modified
[[array objectAtIndex:0] appendString:@" Goodbye, cruel world!"];

// make an immutable version of the mutable string
NSString *copyOfTheCopy =[NSString stringWithString:[array objectAtIndex:0]];

NSLog(@"%@", [array objectAtIndex:0]);

// just to make sure...
[[array objectAtIndex:0] appendString:@"Got you!"];

NSLog(@"%@", [array objectAtIndex:0]);
NSLog(@"%@", copyOfTheCopy);

出力は次のようになります。

2013-11-03 21:16:06.099 SomeProject[7045:303] Hello, world! Goodbye, cruel world!
2013-11-03 21:16:06.100 SomeProject[7045:303] Hello, world! Goodbye, cruel world!Got you!
2013-11-03 21:16:06.100 SomeProject[7045:303] Hello, world! Goodbye, cruel world!

Gabriele Petronellaによって与えられた別の回答により、ポインターを使用してそれらの落とし穴の 1 つを取り除く変更を行うことになりました。これは NSMutableString へのポインターを使用せず、代わりに古い文字列から新しい文字列を作成します。申し訳ありませんが、まだあなたに投票できません。あなたは私に何かを教えてくれました:)

于 2013-11-04T03:18:10.733 に答える