How to register a nil default value with NSUserDefaults - macos

I'm calling registerDefaults in my app's static initializer like so:
[[NSUserDefaults standardUserDefaults] registerDefaults:#{
StringKey: #"A Value"
,IntKey: #123
//,NilDefaultKey: nil
}];
The key in question points to an NSData value, which I handle specially when not present. After setting it for the first time though, other calls to retrieve the value for that key still return nil, until I relaunch my app.
What is the correct way to register that the default value should be nil? I tried using [NSNull null], but got the error Attempt to insert non-property list object null for key NilDefaultKey, so I know that's not it.
Should I create my own placeholder value and check for that instead of null? That seems unnecessarily complicated.

Related

keyPathsForValuesAffectingValueForKey not called for all attributes

I am trying to observe changes to a transient attribute in my entity that is dependent on other attributes in the same entity. I want to do this by implementing keyPathsForValuesAffectingValueForKey:. The problem is that this doesn't seem to be invoked for all the attributes in the entity.
My entity has 10 attributes and about 5-6 relationships, and the keyPathsForValuesAffectingValueForKey is called 5 or 6 times with some mix of attributes and relationships, but not all of them, including the transient attribute that I'm really interested in.
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"todoSectionTitle"])
{
NSSet *affectingKeys = [NSSet setWithObjects:#"todoStatus", #"todoStartDate", #"timeNow", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
}
return keyPaths;
}
In this case, keyPathsForValuesAffectingValueForKey is called multiple times, but the key is never "todoSectionTitle" (a transient attribute). Neither is the key ever equal to "todoStatus" though that is a non-transient attribute. The key IS equal to "todoStartDate" in one of the times it is called. It can also be equal
What is the logic behind when keyPathsForValuesAffectingValueForKey: and which keys it is called for, and which keys it ignores? The docs don't seem to shed any light on this.
+keyPathsForValuesAffectingValueForKey: is part of Key-Value Observing (KVO). KVO only needs to call it if something observes a key on an instance of the class which implements it. (That includes any keys observed indirectly. For example, if something observes key "A", it will call the method for key "A". If it indicates that the value of "A" is affected by key "B", then it will also call it for key "B" to see if some other key(s) affect the value of "B".)
If nothing is observing todoSectionTitle or any key whose value is affected by todoSectionTitle, then KVO has no reason to call +keyPathsForValuesAffectingValueForKey: with "todoSectionTitle". In fact, KVO no way to know that there is such a key.
Bindings are built on top of KVO, so all of this applies to them, too.

Enumerate BOOLs from an NSDictionary

I have a .plist file that is loaded into my Xcode project. I have successfully put it in the documents directory of my iPhone while testing it. When I dump the contents into an NSMutableDictionary, and try to enumerate it, I get EXC_BAD_ACCESS crashes. They keys all have BOOLs associated as their values. What am I doing wrong?
My code now:
for (id key in achDict) {
NSLog(#"Achievement:%# done:%#", key, [[achDict objectForKey:key] boolValue]);
}
This always returns EXC_BAD_ACCESS in a crash.
Your NSLog is expecting two objects but you are passing it a string 'key' and an Integer. A Bool Value is not an Object, it returns an Integer value (0 for False and 1 for True). %# is for Objective C Objects. Instead use %d to get Integer Values such as C Booleans.
Change your NSLog statement to:
NSLog(#"Achievement:%# done:%d", key, [[achDict objectForKey:key] boolValue]);
Apple's String Programming Guide has a useful section on String Modifiers

syntax of #selector

ok without using NSInvocation, let's say I have this code:
...
array = [NSMutableArray arrayWithObjects:#"Yoda", #"Jedi", #"Darth Vader", #"Darth Vader", #"Darth Vader" , #"Darth Vader", nil];
SEL removeObjectMessage = #selector(removeObject:inRange:);
//does array allow us to remove and object in a range? if so let's do this
if ([array respondsToSelector:removeObjectMessage]){
NSRange darthVaderRange=NSMakeRange(2, 3);
[array removeObject:#"Darth Vader"inRange:darthVaderRange];
}
how would I perform that last line in the form of the SEL removeObjectMessage? I would have to put a wrapper around the range? I just want to see the syntax of how all that mess would look...
You could do:
if ([array respondsToSelector:removeObjectMessage]){
NSRange darthVaderRange=NSMakeRange(2, 3);
objc_msgSend(array, removeObjectMessage, #"Darth Vader", darthVaderRange);
}
Though that seems pretty fragile...
Unfortunately, if you're passing an argument that's not of type id, you have to use an NSInvocation object. (Otherwise, you could use performSelector:withObject:withObject:.)
There's no existing method that allows you to pass a selector and non-object arguments and get a valid method call. If it were a method that took only object arguments, you would do [array performSelector:removeObjectMessage withObject:#"Darth Vader" withObject:someHypotheticalRangeObject].
But to do it with an NSRange, you would have to either use NSInvocation (which you've said you don't want to do) or create a category on NSObject and use the low-level Objective-C runtime functions to define a method that takes a selector, an object argument and a non-object argument and calls the appropriate method.

Boolean as a property in Cocoa

I'm sure that this has been asked MANY times before, but it's still giving me trouble. I define my class's Boolean property like this:
#property(readwrite,assign) BOOL namesVisible;
And it doesn't give compiler errors, but it NSLogs as (null). Obviously I'm doing something wrong here, but I'm at a loss to what it is.
BOOLs are just chars, either 0 or 1. As such, you don't need to use a storage keyword in the property declaration, so it should be:
#property (readwrite) BOOL namesVisible;
Second, when logging a BOOL, use the int format string, %d, or pass in a string:
NSLog(#"My Boolean: %d, or %#", object.namesVisible, object.namesVisible ? #"Yes" : #"No");
Because you're trying to log it as an object by using %#, and a BOOL isn't an object, and your property's value is NO and you're lucky.
The last part is because you're only passing a BOOL to NSLog, but since your format string says to expect an object pointer, it will read a pointer's worth from the argument stack. Since a pointer is bigger than a BOOL, it's reading more than you passed it. You're lucky that it got zeroes for all four/eight bytes (your NO was only one of them); the result is that it sent its description message to nil, which returned nil for the description string, which prints as “(null)” in the output.
If you'd been unlucky and/or the property's value had been YES, it would have read something that isn't nil, but is nonetheless probably not a pointer to an object that exists. As such, trying to log that would cause a crash, probably of the EXC_BAD_ACCESS variety. If you'd been unlucky and lucky at the same time, you would have printed the description of an actual object, and wondered how the hell your BOOL looked like that.
The solution is one of two things:
NSLog(#"My Boolean property: %d", (int)[myObject myBooleanProperty]);
or:
NSLog(#"My Boolean property: %#", [myObject myBooleanProperty] ? #"YES" : #"NO");
The former casts the Boolean value to a full-size int and prints that value as such (most probably either 0 or 1), whereas the latter will pass either #"YES" or #"NO" as the argument depending on the Boolean value. Since NSString literals are (NSString) objects, the %# formatter becomes the right one.

Cocoa JSON - Check whether array or dictionary

I am using the Cocoa JSON framework ( http://code.google.com/p/json-framework/ ) to communicate with an API.
The problem is that the API returns a dictionary if there is an error but returns an array of the results if it works.
Is there a good way to detect if the JSONValue is an array or a dictionary?
Thanks.
You can use isKindOfClass: to test whether the object is an instance of NSDictionary or of any subclass thereof.
In most circumstances, you would be better served by a respondsToSelector: check, but this is one case where you really are better off testing its class membership.
Of course, you can test whether it's an array instead of whether it's a dictionary; as long as the API you're using only returns an array or dictionary, the effect is the same.
For true robustness, test both array and dictionary membership, and throw an exception or present an error if the object is neither.
Maybe try to check length property.
if (jsonObj.length) {
//doSomeWork
}
Try this:
if ([YourData isKindOfClass:[NSArray class]])
{
NSLog(#"Array format found");
}
else
{
NSLog(#"Dictionary format found");
}

Resources