Как скопировать объект полностью в Objective-C

Проблема копирования объектов в 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