Защитить от добавления объекта в NSMutableArray в общедоступном интерфейсе

Я хочу защитить доступ к NSMutableArray в общедоступном интерфейсе

Я пытаюсь сделать это, определив свойство как NSArray в общедоступном интерфейсе и как NSMutableArray в частном интерфейсе следующим образом:

@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end

@interface Order()
@property (readwrite, strong, nonatomic) NSMutableArray* comments;
@end

Но это не работает, поэтому вместо этого я должен определить свойство в общедоступном интерфейсе NSMutableArray:

@interface Order
@property (strong, nonatomic) NSMutableArray* comments;
@end

Цель состоит в том, чтобы предоставить клиентам API доступ только для чтения к комментариям и полный доступ к таким методам, как addObject: в реализации.

Итак, определение цели более четкое:

  1. Клиент должен иметь доступ к свойству как NSArray без возможности доступа к методам мутации.
  2. Клиент не должен иметь возможности обновлять комментарии, чтобы они указывали на новое значение.
  3. Решение должно быть выполнено без создания лишних структур и копирования массива.

Таким образом, просто вопрос заключался в том, можно ли сделать общедоступное определение свойства более общим (NSArray вместо NSMutableArray).

Есть ли другой чистый способ достичь цели или мне нужно везде использовать NSMutableArray?

РАЗРЕШЕНИЕ

Изучив свой первоначальный вопрос и ответы, я понял, что хотел бы использовать более общий класс NSArray в общедоступном интерфейсе и NSMutableArray в реализации, но это просто невозможно для одного свойства. Так что ответ невозможен.

Поэтому я просто буду использовать одно свойство с NSMutableArray без какой-либо дополнительной защиты.

Но я также выберу наиболее подходящий ответ, который может помочь, если вы действительно предпочитаете защиту простоте и эффективности.


person Vladimir    schedule 31.03.2013    source источник


Ответы (5)


Вам не нужно общедоступное свойство, если все, что вам нужно, это позволить клиентам читать массив.

Просто создайте метод доступа, который возвращает копию вашего закрытого изменяемого массива:

@interface Order : NSObject
- (NSArray *)allComments;
@end

@implementation Order ()
@property (nonatomic, strong) NSMutableArray * comments;
@end

@implementation Order

@synthesize comments;

- (NSArray *)allComments
{
    return [[self comments] copy];
}

@end

Этот шаблон можно увидеть, например, в NSView: constraints и subviews внутренне изменяемы, но доступны для чтения только с помощью одного метода, возвращающего неизменяемый массив.

person jscs    schedule 31.03.2013
comment
Выбор этого ответа как наиболее подходящего для исходного вопроса. Но я не буду использовать копию и не буду создавать лишний геттер — оставлю код немного незащищенным, но чистым *простым с одним свойством. Спасибо за ответ! - person Vladimir; 02.04.2013
comment
Владимир, привет, у меня точно такая же ситуация, как и у тебя. Почему бы вам не пойти с этим решением? Звучит на самом деле отличное решение нашей проблемы; в чем проблема с неглубоким копированием массива? - person kernix; 08.08.2015

Одним из решений было бы объявить свойство доступным только для чтения как NSArray. Затем в своей реализации создайте отдельное доступное для записи свойство на основе NSMutableArray.

В .ч:

@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end

В них:

@interface Order()
@property (strong, nonatomic) NSMutableArray* internalComments;
@end

Вместо того, чтобы синтезировать свойство только для чтения, напишите:

- (NSArray *)comments {
    return [self.internalComments copy];
}

В .m вы делаете все с self.internalComments.

person rmaddy    schedule 31.03.2013

Гораздо более чистое решение, если оно приемлемо для вашего варианта использования, состояло бы в том, чтобы объявить свойство как NSArray, поддерживая его NSMutableArray. Технически клиент может изменить массив, просто приведя его к изменяемому, но вы ясно даете понять, что делать это было бы плохой идеей.

@property — это просто оболочка вокруг двух методов, геттера и сеттера, которые обычно поддерживаются переменной хранения. Самый простой способ реализовать это решение:

@interface Order : NSObject
{
    NSMutableArray *_comments;
}

@property (readonly, strong, nonatomic) NSArray *comments;

- (void)addComment:(Comment *)comment;

@end

@implementation Order
@synthesize comments=_comments; // Can be omitted if you use Xcode 4.4+

- (void)addComment:(Comment *)comment
{
    [_comments addObject:comment];
}

@end

Если вы хотите, чтобы пользователь мог заменить весь массив (order.comments = ...), удалите атрибут readonly свойства и переопределите метод -setComments::

- (void)setComments:(NSArray *)array
{
    [_comments release]; // Can be omitted if you are using ARC
    _comments = [array mutableCopy];
}

Стоит отметить, что, поскольку Objective-C является динамическим языком, невозможно полностью запретить кому-либо доступ к переменной, поскольку вы можете взаимодействуйте напрямую со средой выполнения или вызывайте методы с помощью их селектора, если вы действительно хотите копаться в том, что вам нужно. не положено. Все, что вы действительно можете сделать, это дать понять, что это плохая идея.

person liclac    schedule 04.10.2013

Два решения:

  1. Возвращает неизменяемую копию массива — клиент получает все комментарии за один раз.
  2. Возвращает количество комментариев и отдельных комментариев.

Выбирайте сами, есть аргументы в пользу обоих, и все зависит от того, что вам подходит:

@interface Order : NSObject

// solution one - return a copy
@property (readonly, strong, nonatomic) NSArray* comments;

// solution two - return individual comments
@property (readonly) NSUInteger commentCount;
- (id) comment:(NSUInteger)number;

@end

@interface Order()

// internal property - mutable
@property (readwrite, strong, nonatomic) NSMutableArray* privateComments;

@end

@implementation Order

// solution one - return a copy
- (NSArray *) comments            { return [self.privateComments copy]; }

// solution two - return individual comments
- (NSUInteger) commentCount       { return self.privateComents.count; }
- (id) comment:(NSUInteger)number { return self.privateComment[number]; }

@end
person CRD    schedule 31.03.2013

Если вы не хотите, чтобы клиент вообще мог устанавливать это свойство, объявите его в публичном интерфейсе как readonly. Если вы не хотите, чтобы клиент мог изменять массив, который клиент может прочитать, объявите его как NSArray. Между тем, на вашей стороне вы readwrite и copy. Будучи readwrite, вы можете взять mutableCopy (который будет NSMutableArray), внести свои изменения, а затем снова установить свойство; будучи copy, вы гарантируете, что клиент всегда видит NSArray.

Итак, клиент:

NSArray* arr = order.comments; // ok
[arr addObject: @"ha"]; // no, can't
order.comments = someOtherArray; // no, can't

Внутренний порядок:

NSMutableArray* marr = self.comments.mutableCopy;
[marr addObject: @"ha"];
self.comments = marr; // and it is magically turned back into an NSArray
person matt    schedule 31.03.2013