Найкраща практика впровадження copyWithZone:


77

Я намагаюся прояснити кілька речей у своїй голові щодо реалізації 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?

Відповіді:


101
  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;
}

Звичайно, якщо ви копіюєте ivars, зберігаєте їх або просто призначаєте їм, це має відображати те, що роблять сетери.


36
Який із двох підходів вибрати, залежить від того, чи застосовується суперклас NSCopying. Наприклад, NSObjectні, тому виклик [super copyWithZone: zone]викличе виняток.
Costique

Там сказано /Users/ws403216/Desktop/Demo/Demo/Crime.m:21:28: Немає видимого @ інтерфейсу для 'NSObject' оголошує селектор 'copyWithZone:' Суперклас злочинності.m у моєму випадку - NSObject.
Нітін Малгурі,

11
@NitinMalguri Як зазначалося в попередньому коментарі, вам слід дзвонити лише в тому [super copyWithZone:zone]випадку, якщо батьківський клас підтримує NSCopying, інакше слід викликати [[[self class] allocWithZone:zone] init]та копіювати поля, як потрібно.
Тоні

2
За замовчуванням поведінка копії має бути неглибокою, але ви надали рішення для глибокої копії. Різниця між поверхневою та глибокою копією полягає в тому, що неглибока копія об'єкта копіює лише посилання на об'єкти вихідного масиву та розміщує їх у новому масиві. Глибока копія фактично копіює окремі об'єкти, що містяться в об'єкті. Це робиться шляхом надсилання кожному окремому об’єкту повідомлення „copyWithZone:”.
Тризуб

що може пояснити непорозуміння NSCoping prot
kokos8998

6

За замовчуванням поведінка copyWithZone:методу копіювання методом із наданими SDK-об'єктами є "поверхнева копія". Це означає , що , якщо ви телефонуєте copyWithZone:вNSString об'єкт, він створює неповну копію , але не глибокої копії. Різниця між неглибокою та глибокою копією:

Неглибока копія об’єкта лише копіює посилання на об’єкти вихідного масиву та розміщує їх у новому масиві.

Глибока копія фактично копіює окремі об'єкти, що містяться в об'єкті. Це робиться шляхом надсилання кожному окремому об’єктуcopyWithZone: повідомлення у власному методі класу.

INSHORT: Щоб отримати неглибоку копію, яку ви викликаєте, retainабо strongвсі змінні екземпляра. Щоб отримати глибоку копію, ви викликаєте copyWithZone:всі змінні екземпляра у реалізації вашого користувацького класу copyWithZone:. Тепер це ваш вибір.


0

Як щодо цього, який реалізує глибоку копію:

/// 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;
}

-1

Це моя модель.

#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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.