NSSet -member to check equality of NSValue - objective-c

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

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.

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,

[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.

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.

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:).

Related

Clear description of NSArray isEqual method?

It is unclear to me how NSArray's isEqual method compares elements of the two arrays. Does it check if both arrays contain identical objects (eg. ==) ? or does it compare the contents of both arrays using isEqual on the objects?
I find Apple's docs for this method terse and unclear. I can't find the source for NSArray.m either.
No answer exists in the modern Cocoa documentation, but if you go all the way back to WebObjects 3.5's NSArray documentation, you find this gem:
- (BOOL)isEqual:(id)anObject
Returns YES if the receiver and anObject are equal; otherwise returns NO. A YES return value indicates that the receiver and anObject are both instances of classes that inherit from NSArray and that they both contain the same objects (as determined by the isEqualToArray: method).
The closest thing to an answer outside of the legacy docs is this discussion of object comparison in the Coding Guidelines for Cocoa, which seems to imply that isEqual: and isEqualToWhatever: should do the same thing, with the only difference being the level of type safety.
Still, I recommend filing a bug to ask for the docs to be clarified.
The documentation is clear:
Two arrays have equal contents if they each hold the same number of objects and objects at a given index in each array satisfy the isEqual: test.
That means that isEqual (and not ==) wil be used to test objects for equality.
All objects in Cocoa are compared with -isEqual: by default. The default version of -isEqual: on NSObject, however, does a pointer comparison. So, if the object hasn't properly implemented its -isEqual: and -hash methods, it's going to simply compare pointers.
MAYBE its kinda like this!? (typed here so there're likely typos)
if(![array1 isKindOfClass:[NSArray class]] || ![array2 isKindOfClass:[NSArray class]])
return NO;
if(array1 == array2)
return YES;
if(array1.count != array2.count)
return NO;
for(int i =0; i<array1.count;i++)
if(![array1[i] isEqual:array[i]])
return NO;
return YES;

Detect changes in NSArray in ObjC

I need to detect change in NSArray object - that is if some object was added/removed to/from NSArray or was just edited in-place. Are there some integrated NSArray hash functions for this task - or I need to write my own hashing function for NSArray ? Maybe someone has different solution ? Any ideas ?
All objects have a -hash method but not all objects have a good implementation.
NSArray's documentation doesn't define it's result, but testing reveals it returns the length of the array - not very useful:
NSLog(#"%lu", #[#"foo"].hash); // output: 1
NSLog(#"%lu", #[#"foo", #"bar"].hash); // output: 2
NSLog(#"%lu", #[#"hello", #"world"].hash); // output: 2
If performance isn't critical, and if the array contains <NSCoding> objects then you can simply serialise the array to NSData which has a good -hash implementation:
[NSArchiver archivedDataWithRootObject:#[#"foo"]].hash // 194519622
[NSArchiver archivedDataWithRootObject:#[#"foo", #"bar"]].hash // 123459814
[NSArchiver archivedDataWithRootObject:#[#"hello", #"world"]].hash // 215474591
For better performance there should be an answer somewhere explaining how to write your own -hash method. Basically call -hash on every object in the array (assuming the array contains objects that can be hashed reliably) and combine each together mixed in with some simple randomising math.
You could use an NSArrayController, which is Key-Value-Observing compliant. Unfortunately NSArray is only KVC compliant. This way you can easily monitor the array controller's arrangedObjects property. This should solve your problem.
Also, see this question: Key-Value-Observing a to-many relationship in Cocoa

Cocoa: Testing to find if an NSString is immutable or mutable?

This produces an immutable string object:
NSString* myStringA = #"A"; //CORRECTED FROM: NSMutableString* myStringA = #"A";
This produces a mutable string object:
NSMutableString* myStringB = [NSMutableString stringWithString:#"B"];
But both objects are reported as the same kind of object, "NSCFString":
NSLog(#"myStringA is type: %#, myStringB is type: %#",
[myStringA class], [myStringB class]);
So what is distinguishing these objects internally, and how do I test for that, so that I can easily determine if a mystery string variable is immutable or mutable before doing something evil to it?
The docs include a fairly long explanation on why Apple doesn't want you to do this and why they explicitly do not support it in Receiving Mutable Objects. The summary is:
So don’t make a decision on object
mutability based on what introspection
tells you about an object. Treat
objects as mutable or not based on
what you are handed at the API
boundaries (that is, based on the
return type). If you need to
unambiguously mark an object as
mutable or immutable when you pass it
to clients, pass that information as a
flag along with the object.
I find their NSView example the easiest to understand, and it illustrates a basic Cocoa problem. You have an NSMutableArray called "elements" that you want to expose as an array, but don't want callers to mess with. You have several options:
Expose your NSMutableArray as an NSArray.
Always make a non-mutable copy when requested
Store elements as an NSArray and create a new array every time it mutates.
I've done all of these at various points. #1 is by far the simplest and fastest solution. It's also dangerous, since the array might mutate behind the caller's back. But Apple indicates it's what they do in some cases (note the warning for -subviews in NSView). I can confirm that while #2 and #3 are much safer, they can create major performance problems, which is probably why Apple has chosen not to use them on oft-accessed members like -subviews.
The upshot of all of this is that if you use #1, then introspection will mislead you. You have an NSMutableArray cast as an NSArray, and introspection will indicate that it's mutable (introspection has no way to know otherwise). But you must not mutate it. Only the compile-time type check can tell you that, and so it's the only thing you can trust.
The fix for this would be some kind of fast copy-on-write immutable version of a mutable data structure. That way #2 could possibly be done with decent performance. I can imagine changes to the NSArray cluster that would allow this, but it doesn't exist in Cocoa today (and could impact NSArray performance in the normal case, making it a non-starter). Even if we had it, there's probably too much code out there that relies on the current behavior to ever allow mutability introspection to be trusted.
There's no (documented) way to determine if a string is mutable at runtime or not.
You would expect one of the following would work, but none of them work:
[[s class] isKindOfClass:[NSMutableString class]]; // always returns false
[s isMemberOfClass:[NSMutableString class]]; // always returns false
[s respondsToSelector:#selector(appendString)]; // always returns true
More info here, although it doesn't help you with the problem:
http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html
If you want to check for debugging purposes the following code should work. Copy on immutable object is itself, while it's a true copy for mutable types, that's what the code is based on. Note that since it's calling copy it's slow, but should be fine for debugging. If you'd like to check for any other reasons than debugging see Rob answer (and forget about it).
BOOL isMutable(id object)
{
id copy = [object copy];
BOOL copyIsADifferentObject = (copy != object);
[copy release];
return copyIsADifferentObject;
}
Disclaimer: of course there is no guarantee that copy is equivalent with retain for immutable types. You can be sure that if isMutable returns NO then it's not mutable so the function should be probably named canBeMutable. In the real world however, it's a pretty safe assumption that immutable types (NSString,NSArray) will implement this optimization. There is a lot of code out including basic things like NSDictionary that expects fast copy from immutable types.

Wondering how to deal with nsnumber objects in an arithmatic operation

I saw this thread but wanted to confirm:
How to convert NSNumber objects for computational purposes?
So basically anytime you want to deal with these objects you have to unpack their ivars, and then pack them back up into new objects, presumably NSNumbers?
That seems hella weak(and a large pain in the backside, no?).
How do you folks work with these?
Do you avoid them? Subclass them? is there mutable versions?
This just seems like a lot of work to deal with them, would love to hear their benefits and ways more experienced programmers have used them, or what tactics they have used to avoid using them.
Thanks,
Nick
So basically anytime you want to deal with these objects you have to unpack their ivars, and then pack them back up into new objects, presumably NSNumbers?
Yes. (By the way calling doubleValue does not just mean unpack the ivar. There maybe some conversions too.)
That seems hella weak(and a large pain in the backside, no?).
This "boxing" is necessary because primitive numbers by themselves to not support Objective-C (Foundation.framework)'s ref-counting scheme. For example, you have to box a number as NSNumber in order to store them in an NSArray.
Do you avoid them?
You can't.
Subclass them?
You shouldn't, but if you have to, follow how it's suggested:
As with any class cluster, if you create a subclass of NSNumber, you have to override the primitive methods of its superclass, NSValue. Furthermore, there is a restricted set of return values that your implementation of the NSValue method objCType can return, in order to take advantage of the abstract implementations of the non-primitive methods. The valid return values are “c”, “C”, “s”, “S”, “i”, “I”, “l”, “L”, “q”, “Q”, “f”, and “d”.
If all you want is add some convenient methods e.g. -numberByAddingNumber:, use a category:
#implementation NSNumber (MyExtension)
-(NSNumber*)numberByAddingNumber:(NSNumber*)another {
double myVal = [self doubleValue];
double anotherVal = [another doubleValue];
return [NSNumber numberWithDouble:myVal + anotherVal];
}
#end
...
NSNumber* a, *b;
...
NSNumber* c = [a numberByAddingNumber:b];
...
is there mutable versions?
No.
I avoid NSNumbers when I'm going to have to perform arithmetic on a variable. Actually, I avoid them at all times, unless I'm going to be rolling them into Core Data or something.
Now that there are Objective-C Literals in the newest version of clang compiler (version 3.2 up, came with Xcode 4.6 and also can be built from source), you can do stuff like #42 and #(7+35) to "box" NSNumbers.

How can I remove the first element of an array in Objective C?

In Objective C, is there a one-liner or something small to remove (shorten by one) and return the first element of an array, regardless of its index?
I don't know of a method that returns the item removed, but you can do this using a combination of NSArray#objectAtIndex:0 and NSMutableArray#removeObjectAtIndex:0. I suppose you could introduce a new method category on NSMutableArray that implements a shift method.
That would be a poor thing to do.
Objective-C on the iPhone can actually use most of the performance perks of C.
If you look at some of my other posts, you'll see I'm ADAMANTLY against premature optimization, but when you are coding at the C level, there are just some things you don't do unnecessarilly.
Move memory
Duplicate structures
Allocate sparsely populated memory blocks
Inner loops
... (There are lots more, but my C-life is rusty and, as I said, I'm anti-optimization)
What you probably want is a well-implemented queue. Something that pre-allocates a large enough circular memory structure and then has two pointers that track the first and last bytes.
I'd be pretty surprised to hear that Objective-C didn't have a queue data structure.
Also, don't strive for the one-liners. All the stuff about terse code is overrated. If it makes more sense to call a method, so be it.
It's certainly too late to assist the original poster, but if you have a plain NSArray and not an NSMutableArray, this works well:
id myData = myArray.firstObject;
myArray = [myArray subarrayWithRange:NSMakeRange(1, myArray.count - 1)];
Cocoa array objects (NSArray/NSMutableArray) do not provide a one-line equivalent — you would have to read the object first, then remove it. The fact that these classes provide the methods -lastObject and -removeLastObject but not -firstObject and -removeFirstObject should be a reminder that removing from the front of an array is usually an inefficient operation, since the contents must be shifted (copied) one position forward. This is particular true for arrays in C, which are intrinsically tied with pointers.
If you're working with anything but primitive data types and/or very small arrays, you might want to consider that the behavior of "shifting off" the first element is indicative of a queue data structure. For details on how you might create a queue for objects, see this SO question. Personally, my opinion for that question is that a real queue class provides the cleanest programming idiom. You can even define your own method (perhaps as a category on NSMutableArray or another class) that does provide a one-liner to do what you want:
#interface NSMutableArray (QueueOneLiner)
- (id) removeAndReturnFirstObject; // Verbose, but clearer than "shift"
#end
#implementation NSMutableArray (QueueOneLiner)
- (id) removeAndReturnFirstObject {
id object = [[self objectAtIndex:0] retain];
[self removeObjectAtIndex:0];
return [object autorelease];
}
#end
However, by that point the solution will likely cause more overhead than it's worth, depending on the importance you place on simplicity versus performance of the code that uses it.
If you have an array obj *arr where obj is a class/typename and arr is the array, you can just say arr+1 to get the array without the first element.
Use this code,
[arrayName removeObjectAtIndex:0];
this may help you