Confusion with distinct and indistinct object - objective-c

I thought that NSCountedSet counted numB and numC twice in the frequency because they had the same value, so I created two Fraction objects (not shown) from my class Fraction, and I set their ivars (numerator, denominator) to equal each others but the countForObject: treated them as two distinct objects and counted their frequencies as one each. numA and numB pointed to different places in memory but share the same value, and the two Fraction objects pointed to different places in memory but shared the same value. Why were the Number objects treated as indistinct, but not the Fraction objects?
#import <Foundation/Foundation.h>
#import "Fraction.h"
int main (int argc, char *argv[]) {
#autoreleasepool {
NSNumber *numA = [NSNumber numberWithInt: 1];
NSNumber *numB = [NSNumber numberWithInt: 2];
NSNumber *numC = [NSNumber numberWithInt: 2];
NSArray *array = [NSArray arrayWithObjects: numA, numB, numC, nil];
NSCountedSet *mySet = [[NSCountedSet alloc] initWithArray: array];
for (NSNumber *myNum in mySet) {
NSLog(#"Number: %i Frequency: %lu", [myNum intValue], [mySet countForObject: myNum]);
}
}
return 0;
}
2012-08-05 17:44:58.667 prog[1150:707] Number: 1 Frequency: 1
2012-08-05 17:44:58.669 prog[1150:707] Number: 2 Frequency: 2

In order for your custom class to be recognized by NSCountedSet and by the rest of Foundation, you must implement -isEqual: and -hash correctly. By default, they compare pointers, so two objects will never compare equal even if they represent the same data. A naïve implementation of -isEqual: for a Fraction class would probably look like this:
- (BOOL)isEqual: (id)anObject {
#error Naive implementation. Do not use.
if (anObject == self) {
return YES;
} else if ([anObject isKindOfClass: [Fraction class]]) {
Fraction *reducedSelf = [self reducedFraction];
Fraction *reducedOther = [anObject reducedFraction];
if (reducedSelf.numerator == reducedOther.numerator && reducedSelf.denominator == reducedOther.denominator) {
return YES;
}
}
return NO;
}
- (NSUInteger)hash {
#error Naive implementation. Do not use.
Fraction *reducedSelf = [self reducedFraction];
return reducedSelf.numerator ^ reducedSelf.denominator;
}
Note that this would not allow you to compare instances of your Fraction class against instances of NSNumber. Because -isEqual: must be commutative and because equal objects must have the same hash value, you would need to provide an implementation that was compatible with NSNumber (probably by subclassing it and using NSNumber's implementations.)

Related

Sorting an array with each element contains an NSString and an NSNumber (double) entry

I created a NSMutableArray with two elements; the name of a city (string at index 0) and the distance (double at index 1) from my present position.
for (i=0;i<[City count];++i)
{
distanceFromMe = [Location distanceFromLocation:[cityLocation]];
[a addObject:[cityNames objectatIndex:i]];
[a addObject:#(distanceFromMe)]
[cityArray addObject:a]
}
"Chicago", 560.34
"New York", 204.3456
"Syracuse", 50.04
I would like to sort this array by ascending distances.
"Syracuse", 50.04
"New York", 204.3456
"Chicago", 560.34
I used:
[cityArray sortUsingDescriptors:#[ [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES] ]];
But I keep getting an error of an unrecognized selector sent to instance.
In my reading it appears that the method does not need a key since there is only one NSNumber in the array element. I've read a number of different threads on this but none seem to apply.
Any help would be appreciated. Using xcode obj-c not swift.
I'd do it without using a sort descriptor.
[cityArray sortUsingComparator:
^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
NSArray* arr1 = obj1;
NSArray* arr2 = obj2;
return [arr1[1] compare: arr2[1]];
}];
While #JWWalker's solution works, I'd suggest modeling this a little bit differently.
Specifically, create a class to hold the city/distance pairs:
#interface CityDistance
#property(copy) NSString *name;
#property(copy) NSNumber *distance;
#end
#implementation CityDistance
#end
Store each of your city/distance pairs.
CityDistance *cityDistance = [[CityDistance alloc] init];
cityDistance.name = #"New York";
cityDistance.distance = #(560.3);
NSMutableArray <CityDistance *>*cities = [NSMutableArray array];
[cities addObject: cityDistance];
This allows the compiler to do more type checking, you can hang business logic off the class (i.e. maybe a -(NSNumber *)distanceFromCity:(CityDistance *)otherCity; method?), it is less fragile (oops! Forgot to add the distance to my ambiguous array!) and makes the sorting code more clear:
[cityArray sortUsingComparator:
^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
CityDistance* city1 = obj1;
CityDistance* city2 = obj2;
return [city1.name compare: city2.name];
}];

How To Compare Integer to Objective-C enum

- (void)updateCheckBoxes {
NSArray *availableFuncUnits = _scanner.availableFunctionalUnitTypes;
for(int i = 0; i < [availableFuncUnits count]; i++) {
}
}
If I put a breakpoint inside the for loop, the elements of the NSArray * 'availableFuncUnits' are (__NSCFNumber *)(int)0 and (__NSCFNumber *)(long)3.
The array is supposed to contain elements of the following :
enum
{
ICScannerFunctionalUnitTypeFlatbed = 0,
ICScannerFunctionalUnitTypePositiveTransparency = 1,
ICScannerFunctionalUnitTypeNegativeTransparency = 2,
ICScannerFunctionalUnitTypeDocumentFeeder = 3
};
typedef NSUInteger ICScannerFunctionalUnitType;
Shouldn't I be able to do the following?
if([availableFuncUnits objectAtIndex:i] == ICScannerFunctionalUnitType.ICScannerFunctionalUnitTypeDocumentFeeder) {}
But it always gives me an error saying 'Expected identifier or '('.
How can I perform this comparison correctly? Thanks a lot for the help!
There are two problems that I see:
1) The array availableFuncUnits contains NSNumber objects. You cant directly compare them with primitive types (NSUInteger).
So your if should be like this:
ICScannerFunctionalUnitType type = [availableFuncUnits[i] integerValue]
if(type == ICScannerFunctionalUnitTypeDocumentFeeder){}
In your snippet you were comparing the pointer, not the object.
2) The error you were seeing is because the proper way to use enums is:
i = ICScannerFunctionalUnitTypeDocumentFeeder
You can't store integers in an NSArray because array's can only contain objects. To get integers into an array they must be wrapped with NSNumber:
NSInteger a = 100;
NSInteger b = 200;
NSInteger c = 300;
// Creating NSNumber objects the long way
NSArray *arrayOne = [NSArray alloc] initWithObjects:[NSNumber numberWithInteger:a],
[NSNumber numberWithInteger:b],
[NSNumber numberWithInteger:c], nil];
// Creating NSNumber objects the short way
NSArray *arrayTwo = [[NSArray alloc] initWithObjects:#100, #200, #300, nil];
This is relevant you your question because when you extract your NSNumber objects from your array, if you want to then compare them to actual integers, you must convert them back to integers (unwrap them).
NSLog(#"%d", [arrayOne firstObject] == 100); // COMPILER WARNING!!!
NSLog(#"%d", [[arrayOne firstObject] integerValue] == 100); // True
NSLog(#"%d", [[arrayTwo lastObject] integerValue] == 200); // False
This stage appears to be missing in your example.
Finally to compare your integer values with those from an enum, there's no need to reference the enum name, just use the individual values that make up the enum:
[[arrayTwo lastObject] integerValue] == ICScannerFunctionalUnitTypeFlatbed

Checking if NSArray contains block

Is it safe to check for the existence of particular block within NSArray? Let's say we have this code:
int (^blockA)(int) = ^(int x) {
return x;
};
int (^blockB)(int) = ^(int x) {
return x;
};
NSArray *array = [NSArray arrayWithObjects:[blockA copy], [blockB copy], nil];
if ([array containsObject:blockA]) {
NSLog(#"Idx: %d", [array indexOfObject:blockA]);
}
if ([array containsObject:blockB]) {
NSLog(#"Idx: %d", [array indexOfObject:blockB]);
}
The output I get is:
Idx: 0
Idx: 1
So it appears to be working, but I'm not sure why, especially because array contains copies of original blocks whose implementation is identical. Does each block has some internal identifier or what? Elaboration on why this works would be appreciated.
blockA and [blockA copy] will usually not be the same object, so [array containsObject:blockA] will be unlikely to return YES. You'd have to write for example blockA = [blockA copy] before you add it to the array.
The containsObject: method of NSArray compares using isEqual:, it doesn't check for pointer equality. isEqual also works for blocks as you can see in this example:
void (^block)(void) = ^{
NSLog(#"Some Block");
};
__typeof(block) b = [block copy];
NSLog(#"%i", [b isEqual:block]);
this will print 1.

NSDictionary and comparing SetObjects

Hi I'm wondering how to compare the contents of the setObjects stored with keys in an NSDictionary: Here's my code I'm working with:
[INNumbers setObject:#"0,1,2,3,4,5,6,7" forKey:#"0"];
[INNumbers setObject:#"4,5,6,7,8,9,10,11" forKey:#"1"];
I'm wonder how I would compare the 2 keys and find if there are similar numbers like if 7 occurs in both?
//this will get the numbers in the keys for me
id number1 = [INNumbers objectForKey:#"0"];
id number2 = [INNumbers objectForKey:#"1"];
but I'm not sure how to compare what number1 and number2 retrieve.
If I was not force to use strings, I would do it this way:
NSArray *nums0 = #[#1,#2,#3];
NSArray *nums1 = #[#3,#4,#5];
NSMutableSet *intersection = [NSMutableSet setWithArray:nums0];
[intersection intersectSet:[NSSet setWithArray:nums1]];
NSArray *numsInCommon = [intersection allObjects];
numsInCommon will contain #3 that is the number that is in both arrays
In your example, the values stored in the dictionary are string literals, which are hard to parse and test for membership tests repeatedly. Perhaps you wanted to use NSSets:
#import <Foundation/Foundation.h>
int main()
{
NSSet *numbers1 = [NSSet setWithObjects:#0, #1, #2, nil];
NSSet *numbers2 = [NSSet setWithObjects:#0, #4, #5, nil];
if([numbers1 intersectsSet : numbers2]) {
NSLog(#"The two sets have at least one element in common!");
/* Let's obtain the intersection: */
NSSet *common = [numbers1 objectsPassingTest:^BOOL(id obj, BOOL *stop) {
if ([numbers2 containsObject:obj]) {
return YES;
} else {
return NO;
}
}];
for (id o in common) {
NSLog(#"%#", o);
}
}
return 0;
}

Expandable Collections in Objective-C?

I was checking out this question which has this code
- (NSArray *) percentagesRGBArray:(float[]) rgbArray
{
NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
NSNumber *green = [NSNumber numberWithFloat:rgbArray[1] / 255];
NSNumber *blue = [NSNumber numberWithFloat:rgbArray[2] / 255];
NSNumber *alpha = [NSNumber numberWithFloat:rgbArray[3]];
return [NSArray arrayWithObjects:red, green, blue, alpha, nil];
}
and I thought, "that's terrible, what if you have more than three colors?" I know, you don't, but what if you did have count-1 colors and an alpha? Let's say you had [rgbArray count] (does count even work for a real array?) Using only objective-C, what the normal way that you would return an NSArray of n objects?
I just tried to work it out but I still don't have the chops to do this in objective-C. Here's my failed attempt:
- (NSArray *) what:(float[]) rgbArray
{
int len = sizeof(rgbArray)/sizeof(float); // made up syntax
NSLog(#"length is wrong dummy %d", len);
NSNumber *retVal[len];
for (int i=0;i<(len-1);i++) {
NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
retVal[i] = red;
[red release];
}
retVal[len-1] = [NSNumber numberWithFloat:rgbArray[len-1]];
return [NSArray arrayWithObjects:retVal count:len];
}
You can use an NSMutableArray.
You can add & remove items from it and it is a subclass of NSArray so it can be passed to any method expecting an NSArray.
Well, just as arrayWithObjects:count: has count: part, you can do
- (NSArray *) what:(float[]) rgbArray count:(int)len
{
NSMutableArray*result=[NSMutableArray array];
for (int i=0;i<len;i++) {
NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
[result addObject:red];
}
return result;
}
If you want, I can be as close as what you wrote, which would be
- (NSArray *) what:(float[]) rgbArray count:(int)len
{
NSNumber**retVal=malloc(len*sizeof(NSNumber*));
for (int i=0;i<len;i++) {
NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
retVal[i]=red;
}
NSArray*result=[NSArray arrayWithObjects:retVal count:len];
free(retVal);
return result;
}
At this stage, it's not really a question of Objective-C, but is a question of just plain C, right? Your questions are
how to dynamically allocate an array in C and
how to get the size of an array from a function in C.
The answers are
You use malloc.
You can't do that, so you need to pass that to the function.
That's it.
So, if you have a question about how to deal with C arrays, you should ask C experts... there's nothing special about Objective-C, except the method declaration syntax.