5

我有一个小的值类,我创建了很多实例。通常具有相同的值。此类用作一种标识符,因此主要用途是将此类的实例相互比较(通过isEqual:)。

为了节省一些内存和比较时间,我只在 a 中保留唯一实例NSHashTable并使用指针比较而不是isEqual:.

所以我指定的初始化程序如下所示:

- initWithStuff: (NSString *)stuff;
{
    self = [super init];
    if (!self) return nil;

    // ... (actual initialisation code omitted)

   hash = UniquingHashTable();

   static OSSpinLock spinlock = OS_SPINLOCK_INIT;
   OSSpinLockLock( &spinlock );

   id member = [hash member: self];
   if (member) self = member;
   else [hash addObject: self];

   OSSpinLockUnlock( &spinlock );

   return self;
}

UniquingHashTable()如果它尚不存在,则返回全局NSHashTable或通过创建它。[NSHashTable weakObjectsHashTable]weakObjectsHashTable是重要的一点 - 它存储指向对象的弱指针,一旦没有对该对象的其他强引用,这些对象就会自动被删除。初始化是线程安全的,这要归功于dispatch_once.

这很好用,峰值内存使用率显着降低,我所有的测试用例仍然通过。但我不太确定是否可以依靠指针比较来测试是否相等。是否存在我同时获得两个不同实例的情况或竞争条件(因此指针不同)但它们仍然比较相等isEqual:

澄清我有/想要什么:

给定

MyClass *a = [[MyClass alloc] initWithStuff: stringA];
MyClass *b = [[MyClass alloc] initWithStuff: stringB];

我们总是有

[a isEqual: b] == [stringA isEqual: stringB];

新代码不会改变这一点。

我想要实现的是

[a isEqual: b] == (a == b)

这样我就可以用isEqual:更快的指针比较替换 。(是的,我测量了这个,这将很重要)。

对于单线程代码,或者如果我使用的是 aNSMutableSet而不是弱的,NSHashTable这总是可行的。我只是不确定是否有任何竞争条件,以便我可以有这样的情况

[a isEqual: b] && (a != b)

是真的。

4

1 回答 1

4

该代码应该没问题,但我建议进行一些可能的更改。这些基于假设/观察,可能适用也可能不适用。

如果可能,将唯一的代码移动到类 [factory] ​​方法中,并且在 的值上是唯一的stuff。这可能是伪代码,但它似乎stuff是初始化状态的唯一来源。无论如何,这样做会在冲突 [缓存] 情况下保存通过alloc/ 。dealloc

假设没有其他用途UniquingHashTable(),将that =初始化程序的static声明移动到您的(或工厂)方法中。它使代码更清晰且不那么脆弱(因为没有什么可以在外部与哈希表混为一谈)。dispatch_once()initWithStuff:

在缓存查找代码中使用串行 GCD 队列进行序列化;队列真的很便宜,而且通常不必越过用户->内核障碍来排队和执行一个块。

哈希表将使用hash和的组合isEqual:来准确确定对象相等性(实际上是一个实现细节......因为弱哈希表使用对象个性,isEqual:将用作确定相等性的最终决定)。因此,哈希冲突应该不是问题。如果要确保具有不同地址的两个对象不相等,请isEqual:相应地实现(其中两个相等的对象必须具有相同的哈希,两个不相等的对象可能具有相同的哈希值)。


通常首先测试指针相等性的实现,isEqual:并且只有在失败时,它才会进入测试内部状态所需的兔子洞以确保相等。

如果你有一个 custom isEqual:,它也应该这样做。

在冲突情况下,您的代码(如最初实现的那样)已经产生了 a mallocand free(via +allocand )的开销。-dealloc与几次调用相比,这将是一个看似巨大的开销isEqual:(假设您的哈希表不是非常重的碰撞)。

您如何测量isEqual:与指针比较的开销?

于 2013-04-01T16:00:23.890 回答