NSSet -member to check equality of NSValue

醉酒当歌 提交于 2019-12-23 15:03:09

问题


I have a NSSet containing many thousands of NSValue objects (wrapping CGPoints). I would like to very quickly find if a given CGPoint value exists in the NSSet. It seems to me that the member: method of an NSSet might do the job here, except that it checks for equality using isEqual:. NSValue objects use isEqualToValue:, and so when I execute the code:

[mySet member:valueToCheck];

it actually causes Xcode to crash.

1) Is there some way to use a custom equality check to make this work for NSValue objects?

2) Is this even the best approach (i.e. is member: quick enough in the first place)? The scenario is that I have a NSSet containing a large number of points representing pixels on the screen (iPad). Later on I need to bombard that set with many thousands of points per second to see if they exist in the set. My approach seems crude for achieving this. I thought about creating something like a huge 2-dimensional bit array, with each index representing a pixel on screen. Once I know the point I'm testing for, I can just jump straight to that point in the array and check for a 1 or 0... does this sound better or worse?

Thanks


回答1:


Can you get this to a simple reproducible case? For example, I just tried:

NSValue *v = [NSValue valueWithCGPoint:CGPointMake(1, 1)];
NSSet *s = [NSSet setWithObject:v];
NSLog(@"%@", [s member:[NSValue valueWithCGPoint:CGPointMake(1, 1)]]);

But it works just fine.

edit

-isEqual: is not the problem:

NSValue *v1 = [NSValue valueWithPoint:NSMakePoint(1, 1)];
NSValue *v2 = [NSValue valueWithPoint:NSMakePoint(1, 1)];
NSLog(@"%d", [v1 isEqual:v2]); //logs "1"

-hash is not the problem:

NSLog(@"%d", ([v1 hash] == [v2 hash])); //logs "1"

They are different objects:

NSLog(@"%d", (v1 != v2)); //logs "1"

The problem is in your code. Try cleaning and rebuilding.




回答2:


To answer no. 2:

I don't know how NSSet is implemented internally, but considering that you know you are storing points (with X and Y), I think you would be better by implementing your own partitioning algorithm. Personally I would choose my own implementation over NSSet if you say you have thousands of points.

Storing huge 2-dimensional arrays for each pixel, would probably be the fastest way, but it will kill you in terms of memory consumption. You need something fast, but also lightweight.

There are a lot of algorithms out there and you can find them by searching "spatial partitioning algorithms" on wikipedia or google. It also depends on your programming skills, and how much time you are willing to invest in this.

For example, a pretty simple one would be to implement a quad-tree, where you start by diving your screen(or area) in 4 equal parts. Then if and where is needed, you divide that specific cell also in 4 parts. And you do this until each cell contains a small enough number of points so that you can brute-force test all of them. You can find a very good description on wiki: http://en.wikipedia.org/wiki/Quadtree

Hope this helps,




回答3:


[mySet member:valueToCheck] should not be crashing. NSValue's isEqual: works fine when I try it here, and in fact probably calls isEqualToValue: when given another NSValue to compare to. Is valueToCheck really an NSValue, or is it a CGPoint?

There is no way to override the default hash and comparison methods for NSSet. But NSSet is toll-free bridged with CFSetRef, and you can easily specify custom hashing and comparison methods there:

CFSetCallBacks callbacks = kCFTypeSetCallBacks;
callbacks.equal = customEqualFunction;
callbacks.hash = customHashFunction;
NSMutableSet *set = (NSMutableSet *)CFSetCreateMutable(NULL, 0, &callbacks);

The constraints on these functions are presumably the same as on NSObject's hash and isEqual: methods, anything that is equal must have the same hash. The C-style prototypes for customEqualFunction and customHashFunction are described here and here.




回答4:


One solution would be to subclass NSSet and override member: to do your own comparison. Your own comparison could then simple call isEqualToValue:. Have a look at the subclassing notes in the NSSet documentation.

Another approach would be to add a category to NSValue that implements isEqual:. In this case I'd prefer subclassing because it's a more constrained solution.




回答5:


It's not just a problem with -isEqual:, you may also have an issue with the -hash method. If you want to use an NSSet, you should probably create a custom class that wraps the CGPoint. -isEqual: is then trivial and -hash could be implemented by some method of combining the bits of both coordinates and then treating them as a NSUInteger.

You'll also want to implement the NSCopying protocol which is also trivial if your points are immutable (just retain and return self in -copyWithZone:).



来源:https://stackoverflow.com/questions/5719362/nsset-member-to-check-equality-of-nsvalue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!