2012-02-07 48 views
0

例如對象是這樣的:如何循環返回NSObject中的屬性?

MyUser: NSObject{ 
    NSString *firstName; 
    NSString *lastName; 
    NSString *gender; 
    int  age; 
} 

,我想比較用戶,如果他們的屬性是一樣的,我把它當作平等的......而不是寫一個靜態方法比較足夠的屬性一個接一個,我可以有一個懶惰的方式來獲得所有的屬性來比較自己,謝謝。

+0

另請參見:[「重寫isEqual的最佳實踐:哈希」](http://stackoverflow.com/q/254281/) – outis 2012-02-07 01:53:12

回答

-1

爲了進行比較,這是你想要避免寫的東西。

-(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]; 
} 

使用異或是一種非常簡單的哈希結合方式,我主要將它作爲替代品包含在其中。這可能會影響散列值的質量,具體取決於底層散列函數的分佈情況。如果哈希分佈一致,那應該沒問題。還要注意組合哈希僅適用,因爲內容相同的NSString具有相同的哈希值。這種方法不適用於所有類型;特別是,它不適用於使用默認實現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]; 
} 

其次,請確保您的課程支持fast enumeration protocol。如果沒有,您可以通過使用反射(使用Objective-C運行時函數)來實現-countByEnumeratingWithState:objects:count:以獲得類的實例的list of properties。例如(部分來自 「Implementing 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實現中都存在危險,計算基於屬性值的散列值。從蘋果公司的文件上hash

如果一個可變對象添加到使用散列值來確定對象的集合中的位置集合,由對象的哈希方法返回的值不能同時被改變的對象在收藏。因此,散列方法不能依賴任何對象的內部狀態信息,或者必須確保對象在集合中時對象的內部狀態信息不會更改。

既然你想isEqual:每當兩個對象具有相同的屬性值是真實的,哈希方案必須直接或間接依賴於對象的狀態,所以有解決這個危險沒有得到。

+0

雖然從「力學」的角度來看這很有趣,但它並不是一個好的通用建議。使數據結構實現NSFastEnumeration只是爲了支持一個懶惰的人的isEqual:實現只是可怕的API設計。這個問題無所事事,可悲的是沒有免費的午餐。即使默認情況下提供標準POD樣式相等性測試的語言/運行時也不會濫用像枚舉這樣的機制。摘要中這是一種可怕的方法。 – ipmcc 2012-02-07 02:59:16

+0

@ipmcc:對於一類只有幾個屬性,我完全同意。有人可能能夠爲具有幾十個屬性的類提供上述方法的情況(儘管此時該類可能存在其他設計問題)。這看起來就是那些KISS情況之一,因此我的答案是最後一點。 – outis 2012-02-07 03:05:40