使用“copy”属性来维护一个不可变的NSString
在Objective-C中,我对iOS开发和编程非常陌生。 我一直在做应用程序开发库的练习。
这是我目前正在尝试理解的练习。 3.testing如果将可变string设置为人的名字,会发生什么情况,然后在调用修改后的sayHello方法之前对该string进行变异。 通过添加复制属性更改NSString属性声明并再次testing。
我试图做到这一点,但是我修改的NSString实际上改变了,尽pipe使用了copy属性。
这里是我的声明和实现以及我的testing代码。
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])); } }
考虑这个较短的例子(运行在CodeRunner btw):
#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 } }
我将这个名字指向一个可变string,然后附加一些字符。 结果,名称在对象内部发生了变化。 但是,如果在声明属性时用强制replace为强制,则只会为Person对象创build一个新的不可变string。
这个故事的寓意是,当有人传递一个对象,然后该对象改变时,使用副本可以防止副作用。
消息-[NSString copy]
会在传递可变string(NSMutableString)时产生副本,或者在不可变时(NSString)保留副本。 因此,在声明NSString属性时总是要复制:
@property (nonatomic,copy) NSString *string; // OK @property (nonatomic,strong) NSString *string; // strong should be copy
我在做同一本书时遇到了这个问题。 我添加了副本,发生了完全相同的事情,当我将某些东西添加到用于firstName的NSMutableStringvariables时,它会不断变化。 然后我读了这个部分:
如果您需要直接设置复制属性的实例variables,例如在初始化方法中,请不要忘记设置原始对象的副本:
-(id)initWithSomeOriginalString:(NSString *)aString { self = [super init]; if (self) { _instanceVariableForCopyProperty = [aString copy]; } return self; }
所以,我回到了我的XYZPerson.m,看着我的init代码。
我变了:
- (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:它是正确的方式! 它创build了一个NSMutableString的副本,当我在方法调用之前将其附加到它的末尾时,我没有使用它。
我想你是误解了什么副本。
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
是副本,当string
被改变时它不会改变, lastName
不是复制,所以当可变stringstring
被改变时,值被改变。
这里发生的事情是lastName
和string
是同一个对象,所以当string
被改变时, lastName
被改变为副作用。 这被认为是非常糟糕的,你永远不会想要这样的行为。 使用副本确保firstName
和string
是不同的对象,对string
更改不能影响firstName
。