Проблема копирования объектов в Objective-C
В этом примере переменная account1 является просто указателем:BankAccount *account1;
BankAccount *account1 = [[BankAccount alloc] init];
Такой код, вместо копирования объекта скопирует его адрес в памяти:
BankAccount *account2;
account2 = account1;
Обе переменные будут указывать на один и тот же объект.
Решение 1 - поверхностное копирование (shallow copies)
Большинство классов унаследованы от базового класса NSObject. Преимущество такого наследования в том что наследуются методы для создания, управления, и манипулирования объектами. Два таких метода copy и mutableCopy. Эти методы используют <NSCopying> Protocol. Протокол определяет что должно быть реализовано в объекте чтобы он был копируемым этими методами.
Классы из Foundation Framework уже обычно работают по <NSCopying> Protocol. Поэтому для них можно вызывать copy и mutableCopy методы:
NSString *myString1 = @"Hello";
NSString *myString2;
myString2 = [myString1 mutableCopy];
Таким образом мы получили изменяемую копию объекта myString1.
Если не реализовать протокол <NSCopying> то при вызове методов copy и mutableCopy будет появляться ошибка:
*** -[BankAccount copyWithZone:]: unrecognized selector sent to instance 0x1034f0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[BankAccount copyWithZone:]: unrecognized selector sent to instance 0x1034f0'
Это потому что эти методы вызывают метод copyWithZone.
Как должна выглядеть @interface секция:
@interface BankAccount: NSObject <NSCopying>
{
double accountBalance;
long accountNumber;
}
-(void) setAccount: (long) y andBalance: (double) x;
-(double) getAccountBalance;
-(long) getAccountNumber;
-(void) setAccountBalance: (double) x;
-(void) setAccountNumber: (long) y;
-(void) displayAccountInfo;
-(id) copyWithZone: (NSZone *) zone;
@end
Как должна выглядеть @implementation секция:
-(id) copyWithZone: (NSZone *) zone
{
BankAccount *accountCopy = [[BankAccount allocWithZone: zone] init];
[accountCopy setAccount: accountNumber andBalance: accountBalance];
return accountCopy;
}
Пример:
int main (int argc, const char * argv[])
{
@autoreleasepool {
BankAccount *account1;
BankAccount *account2;
account1 = [BankAccount alloc];
account1 = [account1 init];
[account1 setAccountBalance: 1500.53];
[account1 setAccountNumber: 34543212];
[account1 displayAccountInfo];
account2 = [account1 copy];
[account2 displayAccountInfo];
}
return 0;
}
Проблема поверхностного копирования
NSArray *myArray1;
NSArray *myArray2;
NSMutableString *tmpStr;
NSMutableString *string1;
NSMutableString *string2;
NSMutableString *string3;
string1 = [NSMutableString stringWithString: @"Red"];
string2 = [NSMutableString stringWithString: @"Green"];
string3 = [NSMutableString stringWithString: @"Blue"];
myArray1 = [NSMutableArray arrayWithObjects: string1, string2, string3, nil];
myArray2 = [myArray1 copy];
tmpStr = [myArray1 objectAtIndex: 0];
[tmpStr setString: @"Yellow"];
NSLog (@"First element of myArray2 = %@", [myArray2 objectAtIndex: 0]); // First element of myArray2 = Yellow
Решение 2 - глубокое копирование (deep copy)
NSArray *myArray1;
NSArray *myArray2;
NSMutableString *tmpStr;
NSMutableString *string1;
NSMutableString *string2;
NSMutableString *string3;
NSData *buffer;
string1 = [NSMutableString stringWithString: @"Red"];
string2 = [NSMutableString stringWithString: @"Green"];
string3 = [NSMutableString stringWithString: @"Blue"];
myArray1 = [NSMutableArray arrayWithObjects: string1, string2, string3, nil];
buffer = [NSKeyedArchiver archivedDataWithRootObject: myArray1];
myArray2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];
tmpStr = [myArray1 objectAtIndex: 0];
[tmpStr setString: @"Yellow"];
NSLog (@"First element of myArray1 = %@", [myArray1 objectAtIndex: 0]); // First element of myArray1 = Yellow
NSLog (@"First element of myArray2 = %@", [myArray2 objectAtIndex: 0]); // First element of myArray2 = Red