Защита от добавяне на обект към 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.

В .h:

@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 е просто обвивка около два метода, getter и setter, които обикновено се поддържат от променлива за съхранение. Най-простият начин за прилагане на това решение би бил:

@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