7

私は、Objective-C での iOS 開発とプログラミングに非常に慣れていません。アプリ開発ライブラリで演習を行っています。

これは、私が理解しようとしている現在の演習です。3. 変更可能な文字列を個人の名として設定し、変更した sayHello メソッドを呼び出す前にその文字列を変更するとどうなるかをテストします。copy 属性を追加して NSString プロパティ宣言を変更し、再度テストします。

私はこれをしようとしましたが、変更した NSString は実際には copy プロパティ属性の使用にもかかわらず変更されます。

ここに、私の宣言と実装、およびテスト コードを示します。

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

@interface XYZPerson : NSObject

@property (copy) NSString *firstName;
@property NSString *lastName;
@property NSDate *dob;

- (void)sayHello;
- (void)saySomething:(NSString *)greeting;

+ (id)init;
+ (id)personWithFirstName:(NSString *)firstName lastName:(NSString *)lastName dob:(NSDate   *)dateOfBirth;


@end

//XYZPerson.m
#import "XYZPerson.h"

@implementation XYZPerson

@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@synthesize dob = _dob;


- (void)sayHello {
    [self saySomething:@"Hello World!"];
    NSLog(@"This is %@ %@", self.firstName, self.lastName);
}

- (void)saySomething:(NSString *)greeting {
    NSLog(@"%@", greeting);
}

+ (id)init {
    return [self personWithFirstName:@"Yorick" lastName:@"Robinson" dob:8/23/1990];
}

+ (id)personWithFirstName:(NSString *)firstName lastName:(NSString *)lastName dob:(NSDate   *)dateOfBirth{
    XYZPerson *person = [[self alloc] init];
    person.firstName = firstName;
    person.lastName = lastName;
    person.dob = dateOfBirth;

    return person;
}

@end

//Test code
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "XYZPerson.h"
#import "XYZShoutingPerson.h"


int main(int argc, char *argv[])
{
    @autoreleasepool {
        XYZPerson *guy = [XYZPerson init];
        [guy sayHello];

        //I thought that this change would never be made, but it is everytime I run the code.
        guy.firstName = @"Darryl";
        [guy sayHello];

        XYZShoutingPerson *girl = [XYZShoutingPerson init];
        [girl sayHello];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
4

3 に答える 3

9

この短い例を考えてみましょう (これはCodeRunnerで実行されます):

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic,strong) NSString *name; // strong should be copy
@end

@implementation Person
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        Person *p = [Person new];

        NSMutableString *name = [[NSMutableString alloc] initWithString:@"Alice"];
        p.name = name;
        NSLog(@"%@",p.name); // prints Alice

        [name appendString:@"xxx"];
        NSLog(@"%@",p.name); // prints Alicexxx
    }
}

名前を変更可能な文字列に指定してから、いくつかの文字を追加しています。その結果、オブジェクト内で名前が変更されました。ただし、プロパティを宣言するときに strong を copy に置き換えると、Person オブジェクトのためだけに新しい不変文字列が作成されます。

この話の教訓は、コピーを使用することで、誰かがオブジェクトを通過してからそのオブジェクトが変更されたときの副作用を防ぐことができるということです。

メッセージ-[NSString copy]は、変更可能な文字列が渡された場合 (NSMutableString) にコピーされ、不変の場合 (NSString) に保持されます。したがって、NSString プロパティを宣言するときは常にコピーします。

@property (nonatomic,copy)   NSString *string;  // OK
@property (nonatomic,strong) NSString *string;  // strong should be copy 
于 2012-12-23T03:26:31.370 に答える
2

同じ本を書いていたときに、この問題に遭遇しました。コピーを追加したところ、まったく同じことが起こりました。firstName に使用した NSMutableString 変数に何かを追加すると、変化し続けました。次に、このセクションを読みました。

たとえば初期化メソッドで、コピー プロパティのインスタンス変数を直接設定する必要がある場合は、元のオブジェクトのコピーを設定することを忘れないでください。

-(id)initWithSomeOriginalString:(NSString *)aString { self = [super init]; if (self) { _instanceVariableForCopyProperty = [aString copy]; } return self; }

そこで、XYZPerson.m に戻り、初期化コードを調べました。

私が変更され:

- (id)initWithFirstName:(NSMutableString *)aFirstName lastName:(NSString *)aLastName
        dateOfBirth:(NSDate *)aDate    {
self = [super init];

if (self) {
    _firstName = aFirstName;
    _lastName = aLastName;
    _dateOfBirth = aDate;
}

return self;

}

に:

- (id)initWithFirstName:(NSMutableString *)aFirstName lastName:(NSString *)aLastName
        dateOfBirth:(NSDate *)aDate    {
self = [super init];

if (self) {
    _firstName = [aFirstName copy];
    _lastName = aLastName;
    _dateOfBirth = aDate;
}

return self;

}

そして presto-chango: 正しい方法で動作しました! メソッド呼び出しの前に何かを末尾に追加しても変化しなかった、使用した NSMutableString のコピーを作成しました。

于 2014-02-25T15:45:40.503 に答える
0

あなたはコピーが何をするのか誤解していると思います。

NSMutableString *string = [NSMutableString stringWithString:@"test"];

XYZPerson *guy = [XYZPerson init];
guy.firstName = string;
guy.lastName = string;

[string replaceCharactersInRange:NSMakeRange(1, 1) withString:@"x"];

[guy sayHello];

出力

This is test txst

この例でfirstNameは、コピーは変更されても変更されません。コピーstringlastNameはないため、可変文字列stringが変更されたときに値が変更されます。


ここで起こったことはlastName同じstringオブジェクトであるため、string変更lastNameされると副作用として変更されます。これは非常に悪いと見なされ、この動作は絶対に必要ありません。コピーを使用するfirstNameと、とstringが異なるオブジェクトであり、変更が有効にstringならないことが確認されますfirstName

于 2012-12-23T03:30:26.220 に答える