Decoding with NSCoding: Does Value Exist For Key? - objective-c

When using NSCoding and decoding values, is there a way to tell if a value exists for a given key? In other words, what I'm trying to do is...
if([decoder valueExistsForKey:#"myKey"]) //valueExistsForKey is not a real method :(
{
NSInteger *myInt = [decoder decodeValueForKey:#"myKey"];
}
else
{
//handle special case
}
The issue is that I have old versions of documents in my app that don't have the "myKey" value, and if they don't have it, using 0 for myInt (what happens if you decode a nonexistent key) is not the behavior I want. However, I can't just decode and check if myInt == 0, because it might legitimately be equal to 0.
Since the valueExistsForKey method does not seem to exist, how can I replicate this behavior?

How about containsValueForKey?

Related

What is the rationale behind Apple's pattern of checking return value rather than error?

Apple's guide on Using and Creating Error Objects gives the following code example:
NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:#"html" error:&theError];
if (success == NO) {
// Maybe try to determine cause of error and recover first.
NSAlert *theAlert = [NSAlert alertWithError:theError];
[theAlert runModal]; // Ignore return value.
}
and accompanies it with the statement:
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
I have always been wondering why is this pattern so important? Why should we ALWAYS check the return value? What's wrong if we check whether the error is nil or not?
This design is not terribly unusual, compare also errno in standard C.
The design has a number of potential advantages:
The function does not have to write through a pointer on success. This does not only make the implementation of such functions easier and less error prone, it can also be a small performance advantage (e.g. this prevent CPU caches from being invalidated if the function succeeds).
If we always check that the function failed before accessing an error, we can use that same error pointer for multiple functions. Otherwise, we might get a previous failure rather than the failure of the most recent function.
This makes validation code easier to write. E.g. a function could set the error by default. If all validations pass, the function can simply return success instead of having to reset the error variable.
A function can use the same error pointer when calling other functions, but a failure of these helpers doesn't necessarily imply a failure of the top function.
In your specific case, the variable NSError *theError; has not been initialized. Accessing that variable without assigning to it first would invoke undefined behaviour. The documentation only guarantees that the variable will be set in case of an error.
Imagine you implement a method in terms of a few other methods:
-(BOOL)sendCachedRequestReturningError: (NSError**)err {
BOOL success = [self readCachedRequestReturningError:err];
if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) {
success = [self sendUncachedRequestReturningError:err];
}
return success;
}
Now there are 4 code paths here:
There is a cached request. We'll just return success == YES and all is good.
A non-recoverable error occurs trying to read from the cache. readCachedRequestReturningError: will set err and set success == NO and the caller will call presentError: or whatever
An error occurs trying to do the network request. Same as #2, err is set, and success == NO.
There is no cache, but we can make a network request. readCachedRequestReturningError: will set err to a valid NSError{MYFileDomain, MYFileNotFoundCode}, but then sendUncachedRequestReturningError: will succeed and set success == YES, and not touch err at all, leaving the previous error in it. If you now check err instead of checking the return value, you will think there was an error when all went well.
Note: The code above is grossly simplified, because we only care about errors. Of course in a real program, the methods would probably have another return parameter for the actual reply from the request, or would return the reply or nil instead of a success BOOL. It would also probably check whether err is NULL.

CFStringGetCStringPtr returns NULL on iOS7

I have the following code in my Application:
static void foo(CFStringRef str)
{
CFStringEncoding encoding = CFStringGetSystemEncoding();
const char * cString = CFStringGetCStringPtr(str, encoding);
//.....
}
It's been around since iOS 5, and always worked.
Since iOS 7 release, CFStringGetCStringPtr returns NULL.
Adding the following code, have solved it:
if (cString==NULL)
{
cString = [
((NSString *)str) cStringUsingEncoding:[NSString defaultCStringEncoding]
];
}
Still, I would like to know if anyone knows what causes the problem?
CFStringGetCStringPtr() isn't guaranteed to return non-NULL. From the docs (emphasis added):
Whether or not this function returns a valid pointer or NULL depends on many factors, all of which depend on how the string was created and its properties. In addition, the function result might change between different releases and on different platforms. So do not count on receiving a non-NULL result from this function under any circumstances.
Always have a fallback to CFStringGetCString(), but even better, use Objective-C and NSString's helper methods (e.g. UTF8String).

Sending isEqual: to nil always returns NO

If you send isEqual: to an object that happens to be nil, you always get NO back.
Is this the expected behavior? To be a feature instead of a bug, I would expect it to return YES if the other object is also nil, and NO otherwise? Semantically this seems the correct behavior.
In case my expectations are incorrect, what the recommended proceedure? Check for nil before sending isEqual: (and friends)?
Yes, this is the expected behavior. Any message to nil will return a result which is the equivalent to 0 for the type requested. Since the 0 for a boolean is NO, that is the result.
This is expected behaviour from Objective-C. This basically means that doing this
if ([nil isEqual:nil]) { ... }
evaluates to NO. Even though it doesn't make sense, when looking at it - and even though it's annoying - being able to send messages to nil is actually one of the really cool things about Objective-C. Saves you a lot of code sometimes.
My solution is to define this macro somewhere handy
#define IsEqual(x,y) ((x && [x isEqual:y]) || (!x && !y))
So when I need to test if two objects are equal:
if (IsEqual(obj1, obj2)) { ... }
or not equal:
if (!IsEqual(obj1, obj2)) { ... }
Hope this helps.
It is expected, for two reasons: (1) in Objective-C, sending a message to nil always returns a false-y value (nil, NO, 0, 0.0, etc.; or, more generally speaking, 0, which can be interpreted based on the expected return type of the method); (2) nil represents an unknown value, and two unknown values are not necessarily equal to each other.
If you want to see if an object is nil, use if (!obj) or if (obj == nil).

indexOfObject does not match correctly

I've been stuck with this problem from a couple of days and I can't get myself out of it.
I've searched all over the net, but I couldn't find anything useful to solve my issue.
this is the scenario.
I've got an array of strings containing a bunch of ids fetched from a coredata sqlite db and
I'd like to know the index of a certain element into this array.
My first solution would have been as easy as using indexOfObject
-(NSInteger) getPageId:(NSString *)symbol_id {
NSInteger refId = [myIds indexOfObject:symbol_id];
// .. stuff ..
return refId;
}
now, I don't know why, but the returning value of the function is always NSNotFound.
If I print out the values via NSLog
NSLog(#"%#\n%#", myIds, symbol_id);
I can clearly see that the value I'm searching for figures out into the elements of the array.
I've even tried a dumbest solution, like probing the match via isEqual function into a for loop:
int idx = 0;
for(NSString *tok in myIds) {
if([tok isEqual:synmbol_id])
{
NSLog(#"yay, a match was encountered!!");
return idx;
}
idx++;
}
but the execution never gets into the NSLog.
I dunno where to knock my head.
hope that some of you already figured this out and could explain this to me.
thx in advance
k
Try printing all the elements on the array like this:
for(NSString *tok in myIds) {
NSLog(#"On the array [%#]", tok);
}
Maybe there is a TAB \t, an ENTER \n or something weird in your NSString preventing isEqual message to run as expected. Usually these characters are hard to find on a regular debugger. That's why I'am suggesting to enclose the string in [].

Get String from TextBox and compare

I'm trying something like my first comparison App in Obj-C and i'm already running into trouble.
Well, there is a textBox with unamebox:(id)unb and a textfield NSTextField* myOut;
Well, here was my first try:
if ([unb stringValue] == #"hello") {
[myOut setStringValue:(NSString *)#"hello dude"];
}
else {
[myOut setStringValue:(NSString *)#"What?"];
}
To my shame, this always setzt the text field to "What?"
When I try the isEqualtoString, it doesn't even do anything:
if ([unb isEqualToString:(NSString*)#"hello"]) {
[myOut setStringValue:(NSString *)#"hello dude"];
}
else {
[myOut setStringValue:(NSString *)#"What?"];
}
So, what shall I do to compare it?
by the way, I already read the links which were suggested above. If I missed anything important, I'm sorry
-isEqualToString: is a method on an NSString, not on an NSTextField. You should be getting an error from sending that message.
You want this:
[[unb stringValue] isEqualToString:#"hello"]