Enumerate BOOLs from an NSDictionary - xcode

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

Related

Cocoa Bindings, String value in Defaults bound to a Text Field. How can I programmatically read this string?

I have a cocoa binding between UserDefaults and a TextField setup via IB:
This works great, on every launch of the app the TextField will persist it's value
However, I also want to read this string value programmatically in other places in code. I have tried basically all of the UserDefaults.standard calls:
guard let supplierData = UserDefaults.standard.data(forKey: "singleSupplierDownloadName") else {return}
returns: ▿ 145 bytes
- count : 145 ▿ pointer : 0x0000608000187ae0
- pointerValue : 106102873684704
UserDefaults.standard.string(forKey:) returns nil
object(forKey:) returns this:
▿ Optional<Any>
- some : <62706c69 73743030 d4010203 04050609 0a582476 65727369 6f6e5824 6f626a65 63747359 24617263 68697665 72542474 6f701200 0186a0a2 07085524 6e756c6c 57746573 74696e67 5f100f4e 534b6579 65644172 63686976 6572d10b 0c54726f 6f748001 08111a23 2d32373a 40485a5d 62000000 00000001 01000000 00000000 0d000000 00000000 00000000 00000000 64>
Given this, I decided to try to do something with the Data I retrieved with data(forKey:)
(lldb) po String(data: supplierData, encoding: .ascii)
▿ Optional<String>
- some : "bplist00Ô\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\t\nX$versionX$objectsY$archiverT$top\u{12}\0\u{01} ¢\u{07}\u{08}U$nullWtesting_\u{10}\u{0F}NSKeyedArchiverÑ\u{0B}\u{0C}Troot\u{01}\u{08}\u{11}\u{1A}#-27:#HZ]b\0\0\0\0\0\0\u{01}\u{01}\0\0\0\0\0\0\0\r\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0d"
I played around with the different encoders, and it was all junk. Until I used ascii, you can see that there is hints of English in there, "NSKeyedArchiver", "unarchiver", etc. Made me believe that this data is actually not just a String, but a compressed String (archived)
so then I thought maybe I need to initialize an NSKeyedUnarchiver,
(lldb) po NSKeyedUnarchiver(forReadingWith: supplierData)
<NSKeyedUnarchiver: 0x60800010beb0>
But from there... I don't know what to do. Everything I ask from the NSKeyedUnarchiver returns nil
Am I on the right track?
How can I read this string value stored in the Shared User Defaults Controller via Cocoa Bindings?
The fix was not to use NSKeyedUnarchiveFromData as the value transformer on the binding (or any value transformer, at all).
When you use a value transformer, that makes the binding system transform the value from the view into some other value for the model to store and, when it retrieves a value from the model, to transform it to a value that the view can work with. In your case, a text field works naturally with string values and so does the user defaults system, so there's no need or benefit to transforming to/from data objects.
The main use for transforming via NSKeyedUnarchiveFromData is for values of types that can't be directly stored in property lists, such as color objects.

How to register a nil default value with NSUserDefaults

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.

Parsing NSString to get data out

I have this code...
NSData* myData = producedData;
NSLog(#"Contents of myData: %#", myData);
The log prints
{
"id" = "";
"level" = "level_1";
"handle" = test;
}
How do I get the values for id and level and handle out of this? The original data is a NSString*.
Thanks!
Is it JSON? Use Stig Brautaset's JSON parser http://code.google.com/p/json-framework/
You aren't showing the code that actually obtains the data object, nor are you showing any code related to an NSString.
Are you just assigning a string (producedData) to your myData variable? That won't create a data object; for one thing, it wouldn't know what encoding to use to encode the string's characters into bytes, and more importantly, copying a pointer from one variable to another (which is what myData = producedData does—the variables do not contain the objects themselves, only pointers to them) does not change anything about what the pointer points to. The object will remain a string, even though you told the compiler that myData would point to a data object. The compiler should be warning you about this; you should heed and fix those warnings.
myData definitely is not a data object; if it were, its description of itself would be a hex dump. It is either a string or a dictionary.
The output you showed matches the syntax that an NSDictionary uses to describe itself. On the other hand, the object could be a string containing such a description. (This latter case is what you're expecting.)
If you have a dictionary: You're done! The object is already parsed.
If you have a string: Send it a propertyList message, which will parse the string as a property list and return whatever value is represented in it, which, in this case, will be a dictionary.

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 multiple threads, locks don't work

I have a threadMethod which shows in console robotMotorsStatus every 0.5 sec. But when I try to change robotMotorsStatus in changeRobotStatus method I receive an exception. Where I need to put the locks in that program.
#import "AppController.h"
#implementation AppController
extern char *robotMotorsStatus;
- (IBAction)runThread:(id)sender
{
[self performSelectorInBackground:#selector(threadMethod) withObject:nil];
}
- (void)threadMethod
{
char string_to_send[]="QFF001100\r"; //String prepared to the port sending (first inintialization)
string_to_send[7] = robotMotorsStatus[0];
string_to_send[8] = robotMotorsStatus[1];
while(1){
[theLock lock];
usleep(500000);
NSLog (#"Robot status %s", robotMotorsStatus);
[theLock unlock];
}
}
- (IBAction)changeRobotStatus:(id)sender
{
robotMotorsStatus[0]='1';
}
extern char *robotMotorsStatus;
You have not, in any code that you've shown, set this pointer to point anywhere. (Are you using an SDK for some robotics package that will initialize this variable for you? If so, can you show the configuration setting that tells it that this is the variable to initialize?)
string_to_send[7] = robotMotorsStatus[0];
string_to_send[8] = robotMotorsStatus[1];
If that robotMotorsStatus has not been initialized by an SDK or by code not shown, then these are accessing memory at a random address. It would not surprise me if this were crashing you, and if this were the “exception” you referred to but did not name.
robotMotorsStatus[0]='1';
Same potential problem here.
NSLog (#"Robot status %s", robotMotorsStatus);
This assumes that robotMotorsStatus contains at least one character, and that the last one is a zero byte (the null character)—i.e., that robotMotorsStatus points to a C string. As I've already noted, you have not shown that robotMotorsStatus points to anything definite, and even if it does point somewhere, you have not shown that the contents of that memory are a C string.
If there isn't a null character within the actual bounds of the array, then the array does not contain a C string, and attempting to read the whole C string, as passing the array to a %s formatter does, will cause a crash after you go past the end of the array. If the other two accesses of robotMotorsStatus are not your crash, this one may be.
The solution here is to not only have the pointer variable point somewhere you've intended, but to have a valid C string—including the null character—completely within that space.
Incidentally, these problems have nothing to do with threads.

Resources