Для сравнения, это то, что вы пытаетесь избежать написания.
-(NSUInteger)hash {
return [firstName hash] ^ [lastName hash] ^ [gender hash] ^ age;
}
-(BOOL)isEqual:(id)other {
return [other isKindOfClass:[self class]]
&& age == other.age
&& [gender isEqualToString:other.gender]
&& [firstName isEqualToString:other.firstName]
&& [lastName isEqualToString:other.lastName];
}
Использование XOR - это чрезвычайно простой способ объединения хэшей, и я в основном использую его как замену.Это может повлиять на качество хеш-значения в зависимости от распределения базовых хеш-функций.Если хеши имеют равномерное распределение, все должно быть в порядке.Также обратите внимание, что объединение хэшей работает только потому, что строки NSS, равные по содержанию, имеют одинаковые хэшиЭтот подход не будет работать со всеми типами;в частности, он не будет работать с типами, которые используют реализацию по умолчанию hash
.
. Чтобы обойти написанное выше, сначала измените тип свойства age
на NSNumber
, чтобыне должен рассматриваться как особый случай.Вам не нужно менять ивар, хотя вы можете, если хотите.
@interface MyUser : NSObject {
...
unsigned int age; // Or just make this an NSNumber*
}
...
@property (assign,nonatomic) NSNumber *age;
@implementation MyUser
@synthesize firstName, lastName, gender;
/* if the age ivar is an NSNumber*, the age property can be synthesized
instead of explicitly defining accessors.
*/
@dynamic age;
-(NSNumber*)age {
return [NSNumber numberWithUnsignedInt:age];
}
-(void)setAge:(NSNumber*)newAge {
age = [newAge unsignedIntValue];
}
Во-вторых, убедитесь, что ваш класс поддерживает протокол быстрого перечисления .Если это не так, вы можете реализовать -countByEnumeratingWithState:objects:count:
, используя отражение (с функциями времени выполнения Objective C), чтобы получить список свойств для экземпляров вашего класса.Например (взято частично из " Реализация countByEnumeratingWithState: objects: count: " на Какао с любовью):
#import <objc/runtime.h>
...
@interface MyUser (NSFastEnumeration) <NSFastEnumeration>
-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len;
@end
@implementation MyUser
@synthesize firstName, lastName, gender;
/* defined in the main implementation rather than a category, since there
can be only one +[MyUser initialize].
*/
static NSString **propertyNames=0;
static unsigned int cProperties=0;
+(void)initialize {
unsigned int i;
const char *propertyName;
objc_property_t *properties = class_copyPropertyList([self class], &cProperties);
if ((propertyNames = malloc(cProperties * sizeof(*propertyNames)))) {
for (i=0; i < cProperties; ++i) {
propertyName = property_getName(properties[i]);
propertyNames[i] = [[NSString alloc]
initWithCString:propertyName
encoding:NSASCIIStringEncoding];
}
} else {
cProperties = 0;
// Can't initialize property names. Fast enumeration won't work. What do?
}
}
...
@end
@implementation MyUser (NSFastEnumeration)
-(NSUInteger)
countByEnumeratingWithState:(NSFastEnumerationState *)state
objects:(id *)stackbuf
count:(NSUInteger)len
{
if (state->state >= cProperties) {
return 0;
}
state->itemsPtr = propertyNames;
state->state = cProperties;
state->mutationsPtr = (unsigned long *)self;
return cProperties;
}
@end
Последняя, реализация hash
(с использованием быстрого перечисления) и isEqual:
.Хэш должен вычислять хэши всех свойств, а затем объединять их, чтобы создать хэш для экземпляра MyUser
.isEqual:
может просто проверить, что другой объект является экземпляром MyUser
(или его подклассом), и сравнить хэши.Например:
-(NSUInteger)hash {
NSUInteger myHash=0;
for (NSString *property in self) {
// Note: extremely simple way of combining hashes. Will likely lead
// to bugs
myHash ^= [[self valueForKey:property] hash];
}
return myHash;
}
-(BOOL)isEqual:(id)other {
return [other isKindOfClass:[self class]]
&& [self hash] == [other hash];
}
Теперь спросите себя, какая работа меньше в целом.Если вам нужен единый подход, который будет работать для всех ваших классов, он может быть вторым (с некоторыми изменениями, такими как превращение +initialize
в метод класса на NSObject
, который возвращает массив и длину имени свойства), но впо всей вероятности, первый победитель.
В обеих приведенных выше реализациях hash
существует опасность вычисления хеш-функции на основе значений свойств.Из документации Apple по hash
:
Если изменяемый объект добавляется в коллекцию, которая использует значения хеш-функции для определения позиции объекта в коллекции, значение, возвращаемое методом хеширования объекта, должноне изменяется, пока объект находится в коллекции.Следовательно, либо метод хэширования не должен полагаться на какую-либо информацию о внутреннем состоянии объекта, либо вы должны убедиться, что информация о внутреннем состоянии объекта не изменяется, пока объект находится в коллекции.
Поскольку вы хотитеisEqual:
Чтобы быть истиной, когда два объекта имеют одинаковые значения свойств, схема хеширования должна прямо или косвенно зависеть от состояния объекта, поэтому от этой опасности не обойтись.