Да, вы можете сделать это автоматически. Сначала импортируйте их в свой класс:
#import <objc/runtime.h>
#import <objc/message.h>
Теперь добавьте этот метод, который будет использовать низкоуровневые методы для получения имен свойств:
- (NSArray *)propertyKeys
{
NSMutableArray *array = [NSMutableArray array];
Class class = [self class];
while (class != [NSObject class])
{
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
for (int i = 0; i < propertyCount; i++)
{
//get property
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
//check if read-only
BOOL readonly = NO;
const char *attributes = property_getAttributes(property);
NSString *encoding = [NSString stringWithCString:attributes encoding:NSUTF8StringEncoding];
if ([[encoding componentsSeparatedByString:@","] containsObject:@"R"])
{
readonly = YES;
//see if there is a backing ivar with a KVC-compliant name
NSRange iVarRange = [encoding rangeOfString:@",V"];
if (iVarRange.location != NSNotFound)
{
NSString *iVarName = [encoding substringFromIndex:iVarRange.location + 2];
if ([iVarName isEqualToString:key] ||
[iVarName isEqualToString:[@"_" stringByAppendingString:key]])
{
//setValue:forKey: will still work
readonly = NO;
}
}
}
if (!readonly)
{
//exclude read-only properties
[array addObject:key];
}
}
free(properties);
class = [class superclass];
}
return array;
}
Тогда вот ваши методы NSCoder:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [self init]))
{
for (NSString *key in [self propertyKeys])
{
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
for (NSString *key in [self propertyKeys])
{
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
}
Вы должны быть немного осторожны с этим. Существуют следующие предостережения:
Это будет работать для свойств, представляющих собой числа, числа, объекты и т. Д., Но пользовательские структуры не будут работать. Кроме того, если какие-либо свойства в вашем классе являются объектами, которые сами не поддерживают NSCoding, это не сработает.
Это будет работать только с синтезированными свойствами, но не с иварами.
Вы можете добавить обработку ошибок, проверив тип значения в encodeWithCoder перед его кодированием или переопределив метод setValueForUndefinedKey для более изящной обработки проблемы.
UPDATE:
Я обернул эти методы в библиотеку: https://github.com/nicklockwood/AutoCoding - библиотека реализует эти методы как категорию в NSObject, так что любой класс может быть сохранен или загружен, а также добавляет поддержку кодирования унаследованных свойств, который мой оригинальный ответ не обрабатывает.
ОБНОВЛЕНИЕ 2:
Я обновил ответ, чтобы правильно обрабатывать унаследованные и доступные только для чтения свойства.