Цель-c: почему частные ивары не скрыты от внешнего доступа при использовании KVC - PullRequest
7 голосов
/ 25 мая 2011

После попытки доступа к ивару с помощью KVC я заметил, что на частных и защищенных иварах не было защиты. Неважно, что я ставлю перед ivar (приватное или защищенное ключевое слово) - ivar всегда является общедоступным ivar при использовании метода KVC "setValue". Вот мой код, где все семь иваров и их свойства могут изменяться вне экземпляра класса:

//************ interface file ***************//
@interface MyClass : NSObject {
@public    
  NSNumber *public_num;
@protected 
  NSNumber *protected_num;
@private 
  NSNumber *private_num;
  NSNumber *private_property;
}
@property (retain) NSNumber *public_property;
@property (retain) NSNumber *private_property;
@end

//********* implementation file *********//
@interface MyClass(){
@private
  NSNumber *very_private_num;
}
@property (retain) NSNumber *very_private_property;
@end

@implementation MyClass
@synthesize public_property, private_property, very_private_property;
@end

//****** main **********//
MyClass *myClass = [[MyClass alloc] init];

[myClass setValue:[NSNumber numberWithInt:1] forKey:@"public_num"];
[myClass setValue:[NSNumber numberWithInt:2] forKey:@"protected_num"];
[myClass setValue:[NSNumber numberWithInt:3] forKey:@"private_num"];
[myClass setValue:[NSNumber numberWithInt:4] forKey:@"public_property"];
[myClass setValue:[NSNumber numberWithInt:5] forKey:@"private_property"];
[myClass setValue:[NSNumber numberWithInt:6] forKey:@"very_private_num"];
[myClass setValue:[NSNumber numberWithInt:7] forKey:@"very_private_property"];

NSNumber *l_public_num = [myClass valueForKey:@"public_num"];
NSNumber *l_protected_num = [myClass valueForKey:@"protected_num"];
NSNumber *l_private_num = [myClass valueForKey:@"private_num"];
NSNumber *l_public_property = [myClass valueForKey:@"public_property"];
NSNumber *l_private_property = [myClass valueForKey:@"private_property"];
NSNumber *l_very_private_num = [myClass valueForKey:@"very_private_num"];
NSNumber *l_very_private_property = [myClass valueForKey:@"very_private_property"];

NSLog(@"public_num = %@, protected_num = %@, private_num = %@, public_property = %@, private_property = %@, very_private_num = %@, very_private_property = %@", l_public_num, l_protected_num, l_private_num, l_public_property, l_private_property, l_very_private_num, l_very_private_property);

Результат вывода> public_num = 1, protected_num = 2, private_num = 3, public_property = 4, private_property = 5, very_private_num = 6, very_private_property = 7.

Даже если ivar объявлен в закрытом интерфейсе, он все еще может изменяться вне класса. Итак, как мне обеспечить принудительную инкапсуляцию и «защитить свои ивары от злых других программистов» :)

Ответы [ 5 ]

10 голосов
/ 25 мая 2011

NSObject соответствует NSKeyValueCoding неформальному протоколу.Это определяет setValue:forKey: и valueForKey:.setValue:forKey: и valueForKey: ищут способ доступа к значению ключа в соответствии с определенными правилами поиска , которые включают прямой доступ к переменной экземпляра.Этот прямой доступ контролируется методом accessInstanceVariablesDirectly, который является частью неофициального протокола NSKeyValueCoding, который по умолчанию возвращает YES, позволяя этим методам напрямую обращаться к переменным экземпляра и в результате фактически не делая их закрытыми как таковые.Они все еще являются частными от прямого доступа.

Чтобы решить эту проблему, вам придется переопределить методы, упомянутые выше и определенные в неформальном протоколе NSKeyValueCoding, для предотвращения их доступа.Абизерном, свойства для приватных переменных по-прежнему доступны, поскольку в Objective-C нет понятия приватных методов.

4 голосов
/ 25 мая 2011

Не объявляйте @property для iVar, если вы действительно хотите, чтобы он оставался закрытым.

Это не iVar, который больше не является частным. Среда выполнения Objective C не имеет концепции частных методов. Поскольку при использовании @property и @ synthesize создаются методы доступа, совместимые с KVC, вы всегда можете вызвать методы независимо от того, является ли резервная копия iVar частной или нет.

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

Если вы просто объявите iVar как @private и не сделаете его KVC-совместимым - он останется закрытым. Конечно; тогда вы не можете использовать KVC или KVO на этом iVar, но если вы хотите иметь возможность использовать их, вам не следует объявлять его как частный iVar.

3 голосов
/ 26 мая 2011

Сегодня я заметил интересную вещь.Стивен Кочан в своей книге «Программирование в Objective c 2.0» констатирует один интересный факт об отношениях obj-c и c: «Когда вы определяете новый класс и его переменные экземпляра, эти переменные экземпляра фактически сохраняются внутри структуры».Следовательно, прямой доступ к такому ивару может быть осуществлен с помощью оператора ->.Итак, наконец, я нашел, где такие ключевые слова, как @private и @protected действительно имеют значение.Если я попытаюсь напрямую изменить общедоступное значение ivar в основной программе, то все в порядке - значение будет изменено.Но если я попытаюсь изменить приватный ivar - тогда компилятор предупредит меня о том, что private_num является приватным ivar

myClass->public_num = [NSNumber numberWithInt:11];
myClass->private_num = [NSNumber numberWithInt:11]; // compiler will complain and reject the compilation

Но поскольку механизм KVC по умолчанию все еще разрешает доступ к приватным или общедоступным ivar вне класса, настоящая инкапсуляциязащита должна быть явно применена путем переопределения setValue: forKey: и valueForKey: методы, которые объявлены в неофициальном протоколе NSKeyValueCoding и по умолчанию реализованы в NSObject

1 голос
/ 18 июня 2013

Я добавлю два своих цента к этому старому вопросу.

Я думаю, что @private, @protected также существует для предотвращения доступа к переменной с помощью оператора '->.

Представьте, что у вас есть iVar с именем myPrivateVar, объявленный какниже:

@interface MyClass:NSObject{
  @public
    NSArray *myPrivateVar;
}

Таким образом, даже если вы реализуете метод класса ниже, чтобы вернуть NO, и у вас нет объявленных методов доступа для iVar:

+accessInstanceVariablesDirectly{
    return NO;
}

переменная по-прежнему доступнаесли вы используете myClassObj->myPrivateVar;

С другой стороны, если вы просто сделаете от @public до @private и не реализуете accessInstanceVariableDirectly, переменная по-прежнему доступна дляиспользуя KVC:

[myClassObj valueForKey:@"myPrivateVar"];

(и не доступен через myClassObj->myPrivateVar)

Поэтому, чтобы сделать ваш iVar полностью приватным, он должен быть объявлен как @private, а также должен быть реализован accessInstanceVariablesDirectlyвернуть NO.

0 голосов
/ 25 мая 2011

запретить через переопределение записей kvc:

@implementation MONObject

- (id)valueForKey:(NSString *)key
{
/* enforce it */
    return nil;
}

- (void)setValue:(id)value forKey:(NSString *)key
{
/* enforce it */
}

/* and so on */

@end
...