Лучшая практика при реализации copyWithZone: - PullRequest
73 голосов
/ 28 марта 2012

Я пытаюсь прояснить несколько вещей в моей голове о реализации copyWithZone:, может кто-нибудь прокомментировать следующее ...

// 001: Crime is a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [[[self class] allocWithZone:zone] init];
    if(newCrime) {
        [newCrime setMonth:[self month]];
        [newCrime setCategory:[self category]];
        [newCrime setCoordinate:[self coordinate]];
        [newCrime setLocationName:[self locationName]];
        [newCrime setTitle:[self title]];
        [newCrime setSubtitle:[self subtitle]];
    }
    return newCrime;
}

// 002: Crime is not a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [super copyWithZone:zone];
    [newCrime setMonth:[self month]];
    [newCrime setCategory:[self category]];
    [newCrime setCoordinate:[self coordinate]];
    [newCrime setLocationName:[self locationName]];
    [newCrime setTitle:[self title]];
    [newCrime setSubtitle:[self subtitle]];
    return newCrime;
}

В 001:

  1. Лучше всего написать имя класса напрямую [[Crime allocWithZone:zone] init] или мне следует использовать [[[self Class] allocWithZone:zone] init]?

  2. Можно ли использовать [self month] для копирования iVars или следуетЯ получаю доступ к iVars напрямую, т.е. _month?

Ответы [ 4 ]

96 голосов
/ 28 марта 2012
  1. Вы всегда должны использовать [[self class] allocWithZone:zone], чтобы убедиться, что вы создаете копию, используя соответствующий класс.Пример, приведенный вами для 002, показывает, почему именно: подклассы будут вызывать [super copyWithZone:zone] и ожидать возврата экземпляра соответствующего класса, а не экземпляра суперкласса.

  2. Я получаю доступнепосредственно к ivars, поэтому мне не нужно беспокоиться о каких-либо побочных эффектах, которые я мог бы добавить к установщику свойств (например, генерировать уведомления) позже.Имейте в виду, подклассы могут переопределить любой метод.В вашем примере вы отправляете два дополнительных сообщения на каждый ivar.Я бы реализовал это следующим образом:

Код:

- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [super copyWithZone:zone];
    newCrime->_month = [_month copyWithZone:zone];
    newCrime->_category = [_category copyWithZone:zone];
    // etc...
    return newCrime;
}

Конечно, копируете ли вы ивары, сохраняете их или просто назначаете, они должны отражать то, чтосеттеры делают.

6 голосов
/ 15 апреля 2015

Поведение по умолчанию для метода copyWithZone: с предоставленными SDK объектами - "мелкая копия".Это означает, что если вы вызовете copyWithZone: для NSString объекта, он создаст поверхностную копию, но не глубокую копию.Разница между мелкой и глубокой копией:

Мелкая копия объекта будет только копировать ссылки на объекты исходного массива и помещать их в новый массив.

Глубокая копия фактически копирует отдельные объекты, содержащиеся в объекте.Это делается путем отправки каждому отдельному объекту сообщения copyWithZone: в вашем пользовательском методе класса.

INSHORT: Чтобы получить поверхностную копию, вы вызываете retain или strong для всех переменных экземпляра.Чтобы получить глубокое копирование, вы вызываете copyWithZone: для всех переменных экземпляра в вашей пользовательской реализации класса copyWithZone:.Теперь это ваш выбор.

0 голосов
/ 08 августа 2017

Как насчет этого, который реализует глубокое копирование:

/// Class Foo has two properties: month and category
- (id)copyWithZone:(NSZone *zone) {
    Foo *newFoo;
    if ([self.superclass instancesRespondToSelector:@selector(copyWithZone:)]) {
        newFoo = [super copyWithZone:zone];
    } else {
        newFoo = [[self.class allocWithZone:zone] init];
    }
    newFoo->_month = [_month copyWithZone:zone];
    newFoo->_category = [_category copyWithZone:zone];
    return newFoo;
}
0 голосов
/ 12 августа 2016

Это моя модель.

#import <Foundation/Foundation.h>
@interface RSRFDAModel : NSObject


@property (nonatomic, assign) NSInteger objectId;

@property (nonatomic, copy) NSString *name;

@property (nonatomic, strong) NSArray<RSRFDAModel *> *beans;


@end


#import "RSRFDAModel.h"

@interface RSRFDAModel () <NSCopying>

@end

@implementation RSRFDAModel 


-(id)copyWithZone:(NSZone *)zone {
    RSRFDAModel *model = [[[self class] allocWithZone:zone] init];

    model.objectId = self.objectId;
    model.name = self.name;
    model.beans = [self.beans mutableCopy];

    return model;
}

@end
...