【问题标题】:Objective-C: efficient way to count objects in categoriesObjective-C:按类别计算对象的有效方法
【发布时间】:2012-07-02 20:09:48
【问题描述】:

我在一组类别中有一堆对象。我想知道每个类别有多少。

在另一种语言中,我会创建一个字典,然后迭代对象,为每个对象递增字典中的适当值。但是,因为我不能在 Objective-C 中的 NSDictionary 中存储本机数字类型,这让我不断在 NSNumber 和数字类型之间来回转换:

NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
for (MyObject *obj in objs) {  
    NSNumber *old = [dictionary objectForKey:obj.category];
    NSNumber *new = [NSNumber numberWithInteger:1 + old.integerValue];
    [dictionary setObject:new forKey:obj.category];
}

有没有更有效的方法来做到这一点?

【问题讨论】:

  • 你能把数组obis按类别排序吗?
  • 您是否对此进行了分析并发现它是一个重大瓶颈?特别是考虑到具有小整数值的 NSNumbers 被缓存(并且可能在某些平台上被实现为标记指针),在优化这样的东西之前确实需要有基准和目标。
  • 对不起@vikingosegundo,我不是指Objective-C意义上的“类别”。它只是一些任意值,例如“物种”或“颜色”。你说得对,我应该用一个不同的词。
  • @warrenm:我并不担心它会成为一个瓶颈,我真的只是希望有人有一个很棒的 Objective-C 习语来处理这个问题,因为像这样计算似乎在我的代码中出现了很多。
  • 令人敬畏的 Objective-C 习惯用法是使用 NSNumber。因为它几乎从来都不是瓶颈,所以没有理由不这样做。除了冗长,ObjC 通常并不担心(尽管 Apple 不断添加新功能以消除其中一些问题)。

标签: objective-c algorithm dictionary


【解决方案1】:

您正在寻找NSCountedSet

NSCountedSet *bag = [[NSCountedSet alloc] init];
for (MyObject *obj in objs) {  
    [bag addObject:obj.category];
}
for (id category in bag) {
    NSLog(@"%d instances of %@", [bag countForObject:category], category);
}

【讨论】:

  • 谢谢!是的,这正是我想要的。
  • 在我的例子中,我想要 [bag addObject:obj.category],而不是对象本身,但效果很好。
  • @Aneel 为后代修复它。
【解决方案2】:

您可以使用 NSMutableData 来存储您的计数器:

int                 * count;
NSMutableData       * num;
NSMutableDictionary * dictionary;

dictionary = [[NSMutableDictionary alloc] init];

for (MyObject *obj in objs)
{
    if ((count = [[dictionary objectForKey:obj.category] mutableBytes]) == nil)
    {
        num    = [NSMutableData dataWithCapacity:sizeof(int)];
        count  = [num mutableBytes];
        *count = 0;
        [dictionary setObject:num forKey:obj.category];
    };
    *count += 1;
};

这样可以防止每次更新类别计数时修改字典并分配新的NSNumber

【讨论】:

    【解决方案3】:

    如果事实证明这会浪费大量时间,您可以:

    • 创建一个包含所有唯一类别的数组
    • 使用它的计数来创建一个 C 风格的 int 类型数组
    • 匹配每个传入对象的类别,得到一个数组偏移量,并递增对应的int桶
    • (根据需要优化类别查找,如果这成为新的瓶颈)

    但我当然同意评论者所说的,基本上是先测量。

    【讨论】:

      【解决方案4】:

      如果确实是瓶颈,您可以使用底层的 CFDictionaryRef 而不是 NSMutableDictionary,来创建一个直接存储整数的字典,而不是将它们装箱到 NSNumber 值中。

      阅读有关 CFDictionaryCreateMutable 和 CFDictionaryValueCallBacks 的文档以获取详细信息,但基本思想是您的保留和释放什么都不做,您的描述会即时生成一个 NSNumber(或者只是生成一个 stringWithFormat:"%d"),并且您的平等直接比较整数。

      下面是一个示例,展示了代码的棘手部分:

      #import <CoreFoundation/CoreFoundation.h>
      #import <Foundation/Foundation.h>
      
      CFStringRef intdesc(const void *value) {
        int i = (int)value;
        CFNumberRef n = CFNumberCreate(NULL, kCFNumberIntType, &i);
        CFStringRef s = CFCopyDescription(n);
        CFRelease(n);
        return s;
      }
      
      Boolean inteq(const void *value1, const void *value2) {
        int i1 = (int)value1, i2 = (int)value2;
        return i1 == i2;
      }
      
      int main(int argc, char *argv[]) {
        CFDictionaryValueCallBacks cb = { 0, NULL, NULL, &intdesc, &inteq };
      
        CFMutableDictionaryRef d = 
          CFDictionaryCreateMutable(NULL,
                    0,
                    &kCFTypeDictionaryKeyCallBacks,
                    &cb);
        CFDictionarySetValue(d, @"Key1", (void *)1);
        CFDictionarySetValue(d, @"Key2", (void *)2);
        CFStringRef s = CFCopyDescription(d);
        NSLog(@"%@", s);
        CFRelease(s);
        CFRelease(d);
        return 0;
      }
      

      如果您要做很​​多这样的事情,您可能应该将其封装在 ObjC 中(尤其是如果您使用 ARC),但这留给读者作为练习。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-22
        • 2012-12-16
        • 2015-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多