Frustrating problem with NSTextView - objective-c

I want to print out the Text contents of a NSTextView using the NSLog function in Objective-C. The code I have so far is:
NSString *s=[updateSource textStorage];
NSLog(s);
All I get is the error:
[NSConcreteTextStorage getCharacters:range:]: selector not recognized [self = 0x43f4b0]

Use [updateSource string] instead. [updateSource textStorage] is not an NSString, but rather an NSTextStorage.

It's not the cause of your problem, but you should be using NSLog(#"%#",s); to log your string. The first argument of NSLog should always be a format string, and not the value you're trying to log.
(if you don't, your app will likely crash if the value contains percent characters)

Related

How to do NSLog with variable

What should be the correct format of the below to print *newString ?
NSString *newString = #"Hello this is a string!";
NSLog(#newString);
NSLog works pretty much as a C printf, with the addition of the %# string format specifier, which is meant for objects. Being NSString an object, %# is the right format to use:
NSString *newString = #"Hello this is a string!";
NSLog(#"%#", newString);
For as tempting as it can look, NEVER do
NSLog(newString); //NONONONONO!
since it's a terrible practice that may lead to unexpected crashes (not to mention security issues).
More info on the subject: Warning: "format not a string literal and no format arguments"
The # symbol is just a shorthand for specifying some common Objective-C objects. #"..." represents a string (NSString to be specific, which is different from regular C strings), #[...] represents an array (NSArray), #{...} represents a dictionary (NSDictionary).
On the first line, you've already specified a NSString object using the # sign. newString is now an NSString instance. On the second line, you can just give it's variable name:
NSLog(newString);
You could theoretically just give the variable name, but it is a dangerous approach. If newString has any format specifiers, your app may crash/mess up (or access something that it shouldn't be accesing) because NSLog would try to read the arguments corresponding to the format specifiers, but the arguments don't exist. The safe solution would be NSLog(#"%#", newString);. The first argument to NSLog is now hard-coded and can't be changed. We now know that it will expect a single argument, that we are providing that argument, newString, so we are safe.
Because you've already specified a string and just passing that instance to NSLog, you don't need the # sign again.

UIImage imageNamed size

New to Objective-C, Cocoa, and compiled languages in general so forgive my ignorance:
UIImage *myImage = [UIImage imageNamed:#"1-filter.jpg"];
NSLog(#"myImage.size=%#", myImage.size);
Results in
Thread 1: Porgram received signal: "EXC_BAD_ACCESS".
Why? How do I get the size of a UIImage?
The size is a structure.
Use either size.width, size.height or NSStringFromCGSize(myImage.size) for NSLog output.
This is a subtle, annoying-for-beginners error. (I actually just ran into this last week.)
When you use the format string %# in an NSLog, the argument must be some kind of Objective-C object. (Behind the scenes, when you do NSLog(#"%#", foo), the system calls [foo description] to figure out what string to output. If the variable you pass to NSLog is not an Objective-C object, the system will try to send a message to something that isn’t an object and then throw this error.)
In this particular case, you’re going to be getting an integer, so replace %# with %d in your format string to make everything work okay.
Additionally, as Eugene mentioned, you want to be accessing a part of the size object. So try
NSLog(#"size.height=%d", myImage.size.height);
Edit: this should actually be %f instead of %d, and please read the comments on this answer.

What is the value of the string #""?

I'm writing an iPhone application that uses a lot of editable text fields. I've been learning a lot about UITextFields and NSStrings by reading various references online, but there are some details that still elude me. When a user puts in an incorrect value for one of my text fields, I throw up an error message and put the text field back to the way it was before their input. For empty text fields, I've been doing this:
theTextField.text = #"";
Is this the best way to do this? I just came up with the idea myself, I don't know if there are any problems with it (other than the fact that it seems to work just fine so far).
Also, does #"" have the same value as a "nil" string? In other words, if I set a string to #"" and then call this:
if (myString) {...}
will the statement return true or false?
One last thing. When an NSString is initialized using this:
NSString *myString = [[NSString alloc] init];
what is that string's Length value?
The important thing to understand here is that an NSString with no characters in it, such as #"" or [[NSString alloc] init] is still a valid object. All the consequences that Nick has stated follow from that.
In Objective-C, any valid object will be "True" in a boolean context;* nil is the only false object value.
Since these strings are valid objects, they do have a length, but because they contain no characters, the length is 0.
There are no problems with assigning an empty string object #"" to another string pointer, such as the text of your text field. Since the string with no characters is still a valid NSString object, this is exactly the same as assigning a string which does happen to have characters.
*Unlike so-called "scripting" languages like Python or Perl, where an empty string or collection evaluates to boolean false.
Using
theTextField.text = #"";
is absolutely ok. There should be no problems at all.
if (#"")
will evaluate to true. #"" is not the same as nil.
The length of
NSString *myString = [[NSString alloc] init];
is 0.
This is not an answer to the question, but may be the answer to what you're trying to do.
If you're wondering whether you have to write if(str && str.length) to cover both nil and empty strings, you don't. You may use just if(str.length), since, in Objective-C, unknown messages to nil will return nil (so [a.b.c.d.e.f doStuff] will be nil if any of those values in the chain is nil). There is thus scarce need for specific nullity checks, unless what you want is precisely to determine nullity.
Check NSString's + string.

Reading String from File with Objective C

This one is weird. Hopefully I will ask the right question:
I'm using an md5 method to create a checksum value which I then write to a file. Then afterwards I read the file using this:
NSString * id_From_File = [[NSString alloc]
initWithContentsOfFile:path_to_ID
encoding:NSUTF8StringEncoding
error:&error];
The result gets placed in a NSString which when I print gives me very strange behaviour. For example when I use this to print,
id_with_date = [NSString stringWithFormat:#" %# %#", dateString, id_From_File];
it will print both strings if dateString is placed in the first parameter and id_From_File in the second. If I switch them around (which I need to do) only id_From_File shows.
Edit 1: Example of the switch:
id_with_date = [NSString stringWithFormat:#" %# %#", id_From_File, dateString];
I strongly believe this has something to do with the encoding of the id_From_File string.
Any knowledge!?
Thanks,
NSString should actually be capable of recognizing null characters as the file ending. Did you try to use a different method to load the string. I'd go for this one instead:
- (id)initWithContentsOfFile:(NSString *)path usedEncoding:(NSStringEncoding *)enc error:(NSError **)error
This method automatically detects the file's encoding instead of decoding it with a fixed one.
I've solved the problem!
It has to do with the fact that some strings use a null character to identify the end. Allow me to explain:
Lets say you have two strings, one with a null character at the end and one that doesn't. Depending on which way you order them, they will be read differently when concatenated.
"somestring(null char)" + "another string"
The above, in some code, will read
somestring
if places are switched
"another string" + "somestring(null char)"
then you get
"another string somestring"
My simple hack to fix this was to make a new string with a substring of "some string" which easily got rid of that last char that was causing the bug.
I hope this is clear and helpful!

Warning: "format not a string literal and no format arguments"

Since upgrading to the latest Xcode 3.2.1 and Snow Leopard, I've been getting the warning
"format not a string literal and no format arguments"
from the following code:
NSError *error = nil;
if (![self.managedObjectContext save:&error])
{
NSLog([NSString stringWithFormat:#"%# %#, %#",
errorMsgFormat,
error,
[error userInfo]]);
}
If errorMsgFormat is an NSString with format specifiers (eg: "print me like this: %#"), what is wrong with the above NSLog call? And what is the recommended way to fix it so that the warning isn't generated?
Xcode is complaining because this is a security problem.
Here's code similar to yours:
NSString *nameFormat = #"%# %#";
NSString *firstName = #"Jon";
NSString *lastName = #"Hess %#";
NSString *name = [NSString stringWithFormat:nameFormat, firstName, lastName];
NSLog(name);
That last NSLog statement is going to be executing the equivalent of this:
NSLog(#"Jon Hess %#");
That's going to cause NSLog to look for one more string argument, but there isn't one. Because of the way the C language works, it's going to pick up some random garbage pointer from the stack and try to treat it like an NSString. This will most likely crash your program. Now your strings probably don't have %#'s in them, but some day they might. You should always use a format string with data you explicitly control as the first argument to functions that take format strings (printf, scanf, NSLog, -[NSString stringWithFormat:], ...).
As Otto points out, you should probably just do something like:
NSLog(errorMsgFormat, error, [error userInfo]);
Are you nesting your brackets correctly? I don't think NSLog() likes taking only one argument, which is what you're passing it. Also, it already does the formatting for you. Why not just do this?
NSLog(#"%# %#, %#",
errorMsgFormat,
error,
[error userInfo]);
Or, since you say errorMsgFormat is a format string with a single placeholder, are you trying to do this?
NSLog(#"%#, %#", [NSString stringWithFormat:errorMsgFormat, error],
[error userInfo]);
Final answer: As Jon Hess said, it's a security issue because you're passing a WHATEVER string to a function expecting a format string. That is, it'll evaluate all format specifiers WITHIN the whatever string. If there aren't any, awesome, but if there are, bad things could happen.
The proper thing to do, then, is USE a format string directly, for example
NSLog(#"%#", myNSString);
That way, even if there are format specifiers in myNSString, they don't get evaluated by NSLog.
I don't especially recommend using this, since the warning IS a real warning.. in a dynamic use of the language it's possible to do things runtime to the string (i.e. insert new information or even crash the program).. However it's possible to force suppress if you KNOW that it should be like this and you really don't want to be warned about it..
#pragma GCC diagnostic ignored "-Wformat-security"
Would tell GCC to temporarily ignore the compilation warning.. Again it's not solving anything but there may be times when you can't find a good way to actually fix the problem.
EDIT: As of clang, the pragma has changed. See this: https://stackoverflow.com/a/17322337/3937
Quickest way to fix it would be to add #"%#", as the first argument to your NSLog call, i.e.,
NSLog(#"%#", [NSString stringWithFormat: ....]);
Though, you should probably consider Sixteen Otto's answer.
I've just been passing a nil to negate the warnings, maybe that would work for you?
NSLog(myString, nil);
If you want get rid of the warning "format not a string literal and no format arguments" once and for all, you can disable the GCC warning setting "Typecheck Calls to printf/scanf" (GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO) in your target's build settings.
NSLog() expects a format string, what is getting passed in is just a string. You do not need to use stringWithFormat:, you can just do:
NSLog(#"%# %#, %#", errorMsgFormat, error, [error userInfo])
And that would make the warning go away.
FWIW, this applies to iPhone dev as well. I'm coding against the 3.1.3 SDK, and got the same error with the same problem (nesting stringWithFormat inside NSLog()). Sixten and Jon are on the money.
Just letting anyone know using the appendFormat on NSMutableString can also cause this warning to appear if trying to pass in a formatted string like so:
NSMutableString *csv = [NSMutableString stringWithString:#""];
NSString *csvAddition = [NSString stringWithFormat:#"%#",WHATEVERYOUAREPUTTINGINYOURSTRING];
[csv appendFormat:csvAddition];
So to avoid this warning, turn the above into this:
NSMutableString *csv = [NSMutableString stringWithString:#""];
[csv appendFormat:#"%#",WHATEVERYOUAREPUTTINGINYOURSTRING];
More concise and more secure. Enjoy!
NSLog(#"%# %#, %#",
errorMsgFormat,
error,
[error userInfo]);