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.
Related
I was wondering if there is a way to turn off/avoid 'yellow' warnings in xcode on if let...NSUserDefaults constructs where the key is of a known value.
For example:
if let x = NSUserDefaults.standardUserDefaults().integerForKey("myKey") as? Int {...}
Because of the if let I have to use as?. However, as I am using a known value type (in this case integer) the as? Int is effectively redundant - which is why I am getting the 'yellow warning'.
Thoughts? Is there a better way to code these types of constructs?
My suggestion would be to address the issue instead of silencing the warnings. :)
NSUserDefaults.standardUserDefaults().integerForKey("myKey") does not return an Optional, and the type is known, so you don't need neither optional binding with if let nor type casting with as?.
Just this:
let x = NSUserDefaults.standardUserDefaults().integerForKey("myKey")
will suffice, since .integerForKey just returns 0 if it can't get the actual value.
If you don't like this behavior of getting a default value (I don't), then don't use .integerForKey and use objectForKey with optional binding and type casting instead. Like you were doing first but with .objectForKey replacing .integerForKey. That way you'll get an actual nil if the value for the key is unreachable, not a default value.
if let x = NSUserDefaults.standardUserDefaults(). objectForKey("myKey") as? Int {...}
First of all check always the signature:
⌥-click on the symbol integerForKey: or look at Quick Help.
You will see:
func integerForKey(_ defaultName: String) -> Int
It reveals the return value is a non optional.
Non optionals can retrieved directly as described in Eric's answer without any type casting, optional binding causes an error.
That's one of the essential semantics in Swift.
Here's my test code:
var myDict: [String: AnyObject] = ["k":"v"]
var a = myDict["k"]
var b = a as String
var c = myDict["k"] as String
Here's my Swift playground in Xcode6-beta6:
According to the rules of type inference, doesn't complaining about c logically contradict not-complaining about b?
I believe that this is a bug. Part of what is going on here is that String is not an object. If you change the first line to:
var myDict: [String: Any] = ["k":"v"]
then everything is fine. So, given that string is not an object, casting a variable of type AnyObject? to a String should definitely yield an error. And, since the compiler has already decided that a is of type AnyObject? it should complain about casting a to a String.
Note that if you change the last line to:
var c = myDict["k"] as NSString
the error goes away supporting the notion that the issue is that String is not an object. You get the same complaint if you put an Int as the value in the array and try to cast that to an Int.
Update:
So the plot thickens. If you don't import Foundation or import something that imports Foundation, then you get additional errors. Without Foundation:
So clearly some of this has to do with the dual nature of Strings as non-objects and NSStrings as objects and the ability to use Strings as NSStrings when Foundation is imported.
This has to do with the fact that Dictionary has two subscript overloads:
subscript (key: Key) -> Value?
subscript (i: DictionaryIndex<Key, Value>) -> (Key, Value) { get }
The first is the familiar one where you pass a key and it gives you an optional of the value; and you can use to set the value on a key.
The second one is less common. I believe DictionaryIndex is a kind of iterator into the dictionary, and you can use it as a subscript to directly get the key-value pair at that iterator.
When the compiler can't find an overload that matches (in this case, the first one doesn't match because it returns an optional, which cannot be cast to non-optional String), it just picks one arbitrarily (well, it seems arbitrary to me anyway) to show in the error. In this place, it picks the second one, which you don't recognize. That's why the error seems weird to you.
This works.
var c = myDict["k"] as AnyObject! as String // "v"
To answer your question, the reason Swift complains could be that you are trying to do these two conversions in one go. Remember, the statement var a = myDict["k"] contains an implicit conversion already. The implied conversion is AnyObject?, so the above would also work like this:
var c = myDict["k"] as AnyObject? as String // "v"
Note that the above would lead to a run time error if the key "k" where not defined. You would allow this to return nil by casting to String?.
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
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.
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.