Store int32_t in an NSDictionary - cocoa

How can I store a variable of type int32_t (e.g. for ABPropertyID) in an NSDictionary?
[NSNumber numberWithInt:...] doesn't seem to work.
Thanks
From the comments:
NSLog(#" %# %# ", [NSNumber numberWithLong:kABPersonFirstNameProperty], kABPersonFirstNameProperty);
Prints: 0 (null) Any ideas?

+[NSNumber numberWithInteger:] will hold a 32-bit number nicely on all 32-bit and 64-bit systems. +[NSNumber integerValue] will retrieve it. If you need it unsigned you can use ``+[NSNumber numberWithUnsignedInteger:]`.

I assume here that Dave's issue is specific to the kAB... constants--I may be mistaken in which case ignore the following :)
I believe that the issue here is not that you are using incorrect syntax; it's an order-of-initialization problem. I'm guessing that you are attempting this operation before having initialized the kAB... constants -- e.g., by calling ABAddressBookCreate(). Somewhat confusing the issue is the snippet:
NSLog(#" %# %# ", [NSNumber numberWithLong:kABPersonFirstNameProperty], kABPersonFirstNameProperty);
which should be:
NSLog(#" %# %d ", [NSNumber numberWithLong:kABPersonFirstNameProperty], kABPersonFirstNameProperty);
Here's my output -- prior to calling ABAddressBookCreate():
NSLog(#"Check: %d %d %d", kABPersonFirstNameProperty,kABPersonMiddleNameProperty,kABPersonLastNameProperty);
produces
Check: 0 0 0
after the call, the same log statement produces
Check: 0 6 1

As everyone else has said, NSNumber will work for this. However, there are two other options you should be at least vaguely aware of: CFDictionary (the same thing as NSDictionary under the hood, but it lets you store arbitrary pointers or pointer-sized integers) and NSMapTable.

In this case, using NSNumber seems like the best idea.
For harder cases, you can always use NSValue or NSData to put any type or pointer into a Objective-C object that can be stored in Cocoa collections.
int32_t myInt = 42;
NSValue *myValue = [NSValue value:&myInt withObjCType:#encode(int32_t)];
Number and Value Programming Topics for Cocoa: Using Values
NSValue Class Reference

Related

Is alloc+initWithString: same as copy?

Basically, the question is - are the following essentially the same?
NSString *value1 = ...;
NSString *value2 = [[NSString alloc] initWithString:value1];
and
NSString *value1 = ...;
NSString *value2 = [value1 copy];
Conceptually, yes. However, there is one difference: alloc always creates a new string, whereas copy may return the same string.
In particular, immutable objects, such as immutable strings, are likely respond to copy by returning themselves rather than creating and returning a copy. (After all, if you can't change anything about the original, why would you really need a copy?) Mutable strings will respond to it by creating and returning a copy, as you'd expect.
initWithString: is in the middle: It may release the receiver and return the string you gave it, similar to how copy may return the receiver. However, if that happens, it means you wasted the creation of the string you created with alloc. With copy, you may not need to create any additional objects at all.
About the only reason to use alloc and initWithString: is if you have your own subclass of NSString and want to make an instance of it from an existing string. copy won't use your desired subclass. Since subclassing NSString is practically never warranted in Cocoa, the same is true of using initWithString: (or stringWithString:).
So the bottom line is, just use copy (or mutableCopy). It's shorter, clearer about your intent, and can be faster.
Non-mutable strings are treated a bit special, compared to ordinary objects, so in this case, yes, the two operations are the same.
To wit:
NSString *str1 = #"string";
NSString *str2 = [str1 copy];
NSString *str3 = [[NSString alloc] initWithString: str1];
NSLog(#"str1: %p, str2: %p, str3: %p", str1, str2, str3);
Which gives me the following output:
str1: 0x108a960b0, str2: 0x108a960b0, str3: 0x108a960b0
Since the pointer addresses are the same, we are talking about the same object.

Store selector as value in an NSDictionary

Is there a way to store a selector in an NSDictionary, without storing it as an NSString?
SEL is just a pointer, which you could store in an NSValue:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSValue valueWithPointer:#selector(foo)], #"foo",
nil];
To get the selector back, you can use:
SEL aSel = [[dict objectForKey:#"foo"] pointerValue];
An alternative to Georg's solution would be to convert the selector into an NSString before storing it the NSDictionary:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
NSStringFromSelector(#selector(foo)), #"foo",
nil];
SEL selector = NSSelectorFromString([dict objectForKey:#"foo"]);
This technique, though uses more memory, gives you the ability to serialize the entire NSDictionary as a string via libraries like JSONKit.
An NSDictionary is really just a CFDictionary that retains and releases all keys and values. If you create a CFDictionary directly, you can set it up to not retain and release values. You can typecast a CFDictionaryRef to an NSDictionary * and vice versa.
In case of using UILocalNotification the only way is to use NSSelectorFromString([dict objectForKey:#"foo"]). With valueWithPointer the app crashing when setting userInfo property of UILocalNotification object. Be careful.
While Georg's answer should work, NSValue does also support encoding any value using an Objective-C type encoding string, which has a special way of representing SEL— with a ":" (as opposed to the "^v" produced by -valueWithPointer:, which translates into void *).
source: Objective-C Runtime Programming Guide - Type Encodings
Working off of Georg's solution, the best API-compliant way to put a SEL into an NSValue into an NSDictionary would be:
// store
NSDictionary *dict = #{
#"foo": [NSValue value:&#selector(foo) withObjCType:#encode(SEL)]
};
// retrieve
SEL aSel;
[dict[#"foo"] getValue:&aSel];
The rationale for handling a SEL as its own beast is that the docs describe it as “an opaque type”— which means that its internal workings (even what it's typedefd to) are off-limits to app programmers; Apple may mix it up at any time in the future.
Also, using void *s to force the system to do what you want it to do was useful in C back in the '90s, when most of us didn't know any better.  You're better than that now.
The above approach should only be used if the retrieval of the SEL happens during the program's running duration— you shouldn't be storing that NSDictionary to disk.  If you do need to store SELs long-term (across app launches), you should follow David H's approach and convert it to an NSString.

NSArray multiply each argument

Is the there a way to multiply each NSNumber contained in the array by 10?
Here is what I have so far:
NSMutableArray *vertex = [NSMutableArray arrayWithCapacity:3];
[vertex addObject:[NSNumber numberWithFloat:1.0]];
[vertex addObject:[NSNumber numberWithFloat:2.0]];
[vertex addObject:[NSNumber numberWithFloat:3.0]];
[vertex makeObjectsPerformSelector:#selector(doSomethingToObject:)];
I am not sure what selector to use to do this, please help!
Not easily, no. You would have to loop through the entire thing and replace all those instances of NSNumber with new instances of NSNumber (NSNumber itself is immutable). So, for example:
for( int i = 0; i < [vertex count]; i++ )
[vertex replaceObjectAtIndex:i withObject:[NSNumber numberWithFloat:[[vertex objectAtIndex:i] floatValue] * 10.0f]];
Obviously this is rather hard to read. You are probably better off just using a regular, primitive array of floats if you are going to be manipulating them often (e.g., applying transformations to them).
Short answer - no.
NSNumber of merely a container for a primitive value. It does not do any mathematical work. The makeObjectsPerformSelector method can be used to tell each object in the array to do something. But the class of each of those objects has to have the method the selector is for. NSNumber also does not provide any method for changing the stored value. So even if you added a category to NSNumber to do the math, you would still have to replace the old value in the array with the newly computed one.
I think a better solution would be to add a category method to NSMutableArray to do the work. It would look through the contents, calculate each new value and then replace each array member with the new one.

Attempt to insert nil

Seems like it should be easy to add a boolean to an NSMutableArray.
Assume toDoArray is intialized as an NSMutableArray. The following:
BOOL checkBoxState = NO;
[toDoArray addObject:checkBoxState];
Generates the error "attempt to insert nil."
What's the correct way to add a negative boolean to a mutable array?
As others have said, NSMutableArray can only contain Objective-C objects. They do not have to be subclasses of NSObject, but that is the most typical.
However, long before you ever see the attempt to insert nil. runtime error, you should have seen a compiler warning:
warning: passing argument 1 of 'addObject:' makes pointer from integer without a cast
It is [in a vague and roundabout way] telling you exactly what the problem is; you are trying to stick something into an array that is not a pointer [to an object].
Pay attention to warnings and fix them. Most of the time, the presence of a warning will indicate a runtime error or crash.
NSMutable arrays require an id, a weird part of Objective C. An id is any object, but not a primitive (For example, ints are primitives, while NSArrays are objects, and in extension, ids).
This question might help.
You need using NSNumber to wrap any primitive types (BOOL, int, NSInterger, etc.) before placing it inside collection object (NSArray, NSDictionary, etc.).
Add BOOL to array:
BOOL checkBoxState = NO;
NSNumber* n = [NSNumber numberWithBool:checkBoxState];
[toDoArray addObject:n];
Get BOOL from array:
NSNumber* n = [toDoArray objectAtIndex:0];
BOOL checkBoxState = [n boolValue];

Easy way to set a single character of an NSString to uppercase

I would like to change the first character of an NSString to uppercase. Unfortunately, - (NSString *)capitalizedString converts the first letter of every word to uppercase. Is there an easy way to convert just a single character to uppercase?
I'm currently using:
NSRange firstCharRange = NSMakeRange(0,1);
NSString* firstCharacter = [dateString substringWithRange:firstCharRange];
NSString* uppercaseFirstChar = [firstCharacter originalString];
NSMutableString* capitalisedSentence = [originalString mutableCopy];
[capitalisedSentence replaceCharactersInRange:firstCharRange withString:uppercaseFirstChar];
Which seems a little convoluted but at least makes no assumptions about the encoding of the underlying unicode string.
Very similar approach to what you have but a little more condense:
NSString *capitalisedSentence =
[dateString stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[dateString substringToIndex:1] capitalizedString]];
Since NSString is immutable, what you have seems to be a good way to do what you want to do. The implementations of (NSString*)uppercaseString and similar methods probably look very much like what you've written, as they return a new NSString instead of modifying the one you sent the message to.
I had a similar requirement, but it was for characters within the string. This assuming i is your index to the character you want to uppercase this worked for me:
curword = [curword stringByReplacingCharactersInRange:NSMakeRange(i,1)
withString:[[curword substringWithRange:NSMakeRange(i, 1)] capitalizedString]];
If you profile these solutions they are much slower then doing this:
NSMutableString *capitolziedString = [NSMutableString stringWithString:originalString];
NSString *firstChar = [[capitolziedString substringWithRange:NSMakeRange(0,1)] uppercaseString];
[capitolziedString replaceCharactersInRange:NSMakeRange(0, 1) withString:firstChar];
in testing on an iphone 4 running iOS 5:
#doomspork's solution ran in 0.115750 ms
while above ran in 0.064250 ms;
in testing on an Simulator running iOS 5:
#doomspork's solution ran in 0.021232 ms
while above ran in 0.007495 ms;
Aiming for maximum readability, make a category on NSString and give it this function:
NSString *capitalizeFirstLetter(NSString *string) {
NSString *firstCapital = [string substringToIndex:1].capitalizedString;
return [string stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstCapital];
}
Then in your code where you want it:
NSString *capitalizedSentence = capitalizeFirstLetter(dateString);
This kind of code rarely belongs in the spot where you need it and should generally be factored away into a utility class or a category to improve legibility.

Resources