Why can I define a string as #"this is" #"one string"? - objective-c

This seems to be a perfectly valid program:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString* hey = #"hey"
#"there";
NSLog(#"%#",hey);
[pool drain];
return 0;
}
Compiling this in llvm gcc and apple llvm shows no warnings or errors. Why? It seems one should be warned about this, as I can only see this as chaos-inducing. Especially in a situation like this:
NSArray* a = [NSArray arrayWithObjects:#"one",
#"two,
#"three
#"four",
nil];
You'd expect four entries...but no! Tricky, huh? Is it because you may want to split your string definition across multiple lines?

It's a syntax for multi-line strings in Objective-C.
I cannot definitively answer why the language designer(s) designed it that way, but we can probably assume that they wanted the syntax for Objective-C strings to be analogous to the syntax for C strings.
That is, in C you can do the following for a multi-line string:
char *my_string = "Line 1 "
"Line 2";
And in Objective-C, you can do the following for a multi-line string:
NSString *my_string = #"Line1 "
#"Line2"; // The # on this line is optional.
(Code snippets adapted from https://stackoverflow.com/a/797351/25295)

Is it because you may want to split your string definition across multiple lines?
Yes. It is useful when you want to split the very long string for better code reading i.e. in NSLocalizedString key description.

Related

Objective-C Noob: How to define + call a method in main?

I wrote a method that works inside of an object, but now I want to extract it so that it's just a function. This is my broken command line tool program:
#import <Foundation/Foundation.h>
+ (NSMutableString *)reverseString:(NSString *)originalString {
NSMutableString *reversedString = [[NSMutableString alloc] init];
for (NSInteger i = originalString.length; i > 0; i--) {
[reversedString appendFormat:#"%c", [originalString characterAtIndex:i-1]];
}
return reversedString;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *originalString = #"original string";
NSMutableString *newString = [reverseString:originalString];
NSLog(#"Reversed string: %#", newString);
}
return 0;
}
My question is, how would I call the reverseString method from main()? I don't think I'm defining it properly. Do I have to declare it too? I know that the contents of my method work fine, but I don't know how to define it in a way that allows main to see it.
A "method" is, by definition, part of a class. There are two types, instance methods and class methods. To invoke an instance method, you need, well, an instance of the class. To invoke a class method, you don't need an instance. You can just invoke it directly on a class.
By contrast, there are also "functions". You don't need an instance or a class to invoke a function.
So, it sounds like you want a function. Functions are something that Objective-C inherits from C. The syntax for functions is different from the syntax for methods. Here's how your code might look using a function:
NSMutableString* reverseString(NSString *originalString) {
NSMutableString *reversedString = [[NSMutableString alloc] init];
for (NSInteger i = originalString.length; i > 0; i--) {
[reversedString appendFormat:#"%c", [originalString characterAtIndex:i-1]];
}
return reversedString;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *originalString = #"original string";
NSMutableString *newString = reverseString(originalString);
NSLog(#"Reversed string: %#", newString);
}
return 0;
}
By the way, your code does not "work fine". You can't iterate through a string by what it calls "characters" and treat all of them as independent. What NSString calls "characters" are actually UTF-16 code units. Not all Unicode characters can be expressed as single UTF-16 code units. Some need to use two code units in what's called a surrogate pair. If you split up and reverse a surrogate pair, you'll get an invalid string.
As a separate issue, Unicode has combining marks. For example, "é" can be expressed as U+0065 LATIN SMALL LETTER E followed by U+0301 COMBINING ACUTE ACCENT. Again, if you reorder those "characters", the accent will combine with a different character (or fail to combine at all).
The correct way to iterate through the composed character sequences of a string is to use the -[NSString enumerateSubstringsInRange:options:usingBlock:] method with the NSStringEnumerationByComposedCharacterSequences option.
By "I want to extract it so that it's just a function" you're implicitly saying "I want a C-style function, not an Objective-C class method". C-style functions are declared and called differently (blame history).
static NSMutableString * reverseString(NSString * originalString) {
...
}
...
NSMutableString *newString = reverseString(originalString);

Objective-C, Simple String input from Console?

I honestly did a) search using key words and b) read the 'questions with similar titles' before asking this.
Also I tried to make this question more concise, but I had a hard time doing that in this case. If you feel the question is too wordy, I get it. Just don't try to answer.
I'm trying to write very simple objective-C programs that mirror the basic assignments in my introductory java class. I worked through an objective-c book over the summer and now I want to do lots of practice problems in objective-c, at the same time as I do java practice problems. I'm avoiding the objective-c GUI environment and just want to focus on working with the language for awhile. I still have a lot to learn about how to figure things out.
The program I'm duplicating from my java homework, is a standard type. I ask the user for number input and string input via the console. I was able to get numeric input from the console using an example I found here using scan f. (I will put the couple code lines below). But I'm unsure on how to get console input and store it in a string (NSString). I'm trying to learn to use the apple documentation and found a reference to a scan type command, but I cannot figure out how to USE the command. The one that seems likely is
scanCharactersFromSet:(NSCharacterSet )scanSet intoString:(NSString *)name;
Here's what I understand and works
int age = 0;
NSLog (#"How old are y'all?");
scanf("%d", &age);
NSLog (#"\n Wow, you are %d !", age);
But I don't understand how to pickup an NSString called 'name'. I THINK I'm supposed to make my 'name'a pointer, because the class is NSString.
(BTW I did try using scanf to pickup the string, but the compiler doesn't like me trying to use scanf in conjunction with name. It says that I shouldn't be using 'scanf' because it's expecting a different kind of data. I'm not sure where I found the data type 'i'. I was looking through my text for different ideas. I'm guessing that scanf is related to 'scanfloat' which clearly deals with numeric data, so this is not a big surprise)
I realize that 'scanf' isn't the right command (and I don't really get why I can't even find scanf in the apple documentation - maybe it's C?)
I'm guessing that scanCharactersFromSet might be the right thing to use, but I just don't understand how you figure out what goes where in the command. I guess I tend to learn by example, and I haven't found an example. I'd like to figure out how to learn properly by reading the documentation. But I'm not there yet.
NSString* name ;
scanf("%i", &name);
//scanCharactersFromSet:(NSCharacterSet *)scanSet intoString:(NSString **)name;
...
My book is oriented towards moving me into a gui environment, so it doesn't deal with input.
Thank you for any pointers you can give me.
Laurel
I would recommend ramping up on C. Objective-c is a thin layer over C and that knowledge will pay for itself over and over.
There's multiple ways in C to read:
http://www.ehow.com/how_2086237_read-string-c.html
For example:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
char str[50] = {0}; // init all to 0
printf("Enter you Last name: ");
scanf("%s", str); // read and format into the str buffer
printf("Your name is %s\n", str); // print buffer
// you can create an NS foundation NSString object from the str buffer
NSString *lastName = [NSString stringWithUTF8String:str];
// %# calls description o object - in NSString case, prints the string
NSLog(#"lastName=%#", lastName);
[pool drain];
return 0;
NOTE: the simple scanf is succeptible to buffer overruns. There's multiple approaches around this. see:
How to prevent scanf causing a buffer overflow in C?
Here is what Objective C looks like:
NSString *FNgetInput() {
#autoreleasepool {
return [[[NSString alloc] initWithData:[[NSFileHandle fileHandleWithStandardInput] availableData] encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
}
}
The way to get data from the standard input (or any other file handle) in cocoa is to use the NSFileHandle class. Check the docs for +fileHandleWithStandardInput
Here's how to get user input using Objective-C in 2020:
main.m
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
char str[50] = {0}; // init all to 0
printf("Enter you Last name: ");
scanf("%s", str); // read and format into the str buffer
printf("Your name is %s\n", str); // print buffer
// you can create an NS foundation NSString object from the str buffer
NSString *lastName = [NSString stringWithUTF8String:str];
// %# calls description o object - in NSString case, prints the string
NSLog(#"lastName=%#", lastName);
return 0;
}
return 0;
}
Compile and run:
$ clang -framework Foundation main.m -o app

Are strings defined as constants not NSStrings?

I have a constant defined as:
#define BEGIN_IMPORT_STRING #"Importing Hands!";
But I get an error when I try to concat with:
NSString *updateStr = [NSString stringWithFormat:#"%#%#", BEGIN_IMPORT_STRING, #" - Reading "];
This doesn't happen if I replace it with a string literal
NSString *updateStr = [NSString stringWithFormat:#"%#%#", #"foo", #" - Reading "];
Or a local string
NSString *temp = #"foo";
NSString *updateStr = [NSString stringWithFormat:#"%#%#", temp, #" - Reading "];
You need to remove the semicolon from your #define:
#define BEGIN_IMPORT_STRING #"Importing Hands!"
To the compiler, the resulting line looks like this:
NSString *updateStr = [NSString stringWithFormat:#"Importing Hands!";, #" - Reading "];
Replace
#define BEGIN_IMPORT_STRING #"Importing Hands!";
with
#define BEGIN_IMPORT_STRING #"Importing Hands!"
This is because compiler in your case replaces all occurrences of BEGIN_IMPORT_STRING with #"Importing Hands!";
Aside from the accepted answer (remove semicolon), note that:
#"Foo" is an NSString. You can even send it a message.
#define FOO #"Foo" is a preprocessor macro, not a constant. It's a typing shortcut.
Though macros aren't an uncommon way to avoid retyping the same string, they're an unfortunate holdover. Essentially, they're playing games that aren't necessary anymore.
For repeated strings, I prefer:
static NSString *const Foo = #"Foo;
The const portion of this definition ensures that the pointer is locked down, so that Foo can't be made to point to a different object.
The static portion restricts the scope to the file. If you want to access it from other files, remove the static and add the following declaration to your header file:
extern NSString *const Foo;
Should you be using
NSLocalizedString(#"Importing Hands!", #"Message shown when importing of hands starts");
?
I put it as an answer because this looks like something you would not want to have to go and redo through all your code.

Is the # operator in Objective-C same as the & operator in C?

New to objective C. Trying to understand pointers in Objective C.
my code
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *msg = #"Hello Bhai";
NSLog(#" address is %i for msg", msg);
NSLog(#" which is same address %i for Hello Bhai", #"Hello Bhai");
NSLog(#" which is different address %i for Hello mere Bhai", #"Hello mere Bhai");
NSLog(#" value is %# at the address for msg", msg);
[pool drain];
return 0;
}
output is
[Session started at 2011-04-26 16:31:11 -0400.]
2011-04-26 16:31:11.656 BasicObjC[1523:10b] address is 8240 for msg
2011-04-26 16:31:11.658 BasicObjC[1523:10b] which is same address 8240 for Hello Bhai
2011-04-26 16:31:11.659 BasicObjC[1523:10b] which is different address 8288 for Hello mere Bhai
2011-04-26 16:31:11.660 BasicObjC[1523:10b] value is Hello Bhai at the address for msg
Does this mean # in Objective C is the same as & in C
# is used to denote that string is a NSString literal so you can assign it to NSString object.
If you want to print some address then you should use %p format specifier, not %i, e.g.
NSLog(#" address is %p for msg", msg);
Does this mean # in Objective C is the same as & in C
No, in this context the '#' is used to differentiate an NSString literal from a C char array so that you can choose to use objective-c NSStrings instead of C strings.
Within a format string '%#' is used to get the string representation of an NSObject (from its "description" method) so you can output it to the console.
You are also running into a special behavior of NSString literals. Both of your #"Hello Bhai" string pointers reference the same memory address only because NSString is optimized not to create identical copies of string literals. Other objects will not necessarily behave the same way. In general you shouldn't need to be aware of this sort of optimization because should not make any difference in your use of these objects. If you correctly follow the same retain/release rules and comparisons as with any other object then you'll never notice the difference.
The at sign (#) is something completely different from the & operator. The #"…" syntax is a shorthand for creating an NSString instance from characters given in the quotes.
In general, you will see the # sign used many places in Objective-C, for syntax that is specific to that language and thus not legal in "plain" C:
#implementation
#interface
#property
#selector
#class
As noted by just about everyone else, in this specific case, #"This is a string.", it is used to indicate the start of an Objective-C string literal. It is also used as a format specifier %# to mean "object".
# was originally chosen for these purposes because it wasn't used for anything in C. The "address-of" operator & is perfectly legal in Objective-C*, because Obj-C is a superset of C.
*In fact, it gets used in Cocoa fairly often for indirect returns of values; see for example Error Handling

Objective C error: Passing argument 1 of 'setStringValue:' from incompatible pointer type

Ok here is part of the code that is causing the error:
char charlieReturn[10000];
charlieReturn[10000] = system("osascript /applications/jarvis/scripts/getTextCharlieResponce.scpt");
self.charlieOutput.stringValue = charlieReturn;
The getTextCharlieResponce.scpt returns something like this: "Hi my name is charlie" and maybe sometimes it will be longer than that. The script returns it plain text. I need help FAST!
Thanks in advance! :D
Elijah
Unfortunately there are many problems in your code.
The C function system does not return the standard output of the script as char*.
Even with a function which returns char*, you can't assign it to a array of char as you did:
char* str="aeiou";
char foo[100];
foo[100]=str; /* doesn't work */
foo=str; /* doesn't work either */
Cocoa's string class, NSString*, is not a C string char*, although you can easily convert between the two:
NSString* str=[NSString stringWithUTF8String:"aeiou"];
If you want a string out of a call to an Apple script, you need to do the following:
Prepare an NSAppleScript:
NSDictionary* errorDict;
NSAppleScript* script=[[NSAppleScript alloc]
initWithContentsOfURL:[NSURL fileURLWithPath:#"path/to/script" ]
error:&errorDict];
Execute and get a reply:
NSAppleEventDescriptor* desc=[script executeAndReturnError:&errorDict];
NSString* result=[desc stringValue];
Release the script:
[script release];
Learn C & Objective-C and have fun!
You're trying to assign a character array to what is presumably an NSString. Try this instead:
self.charlieOutput.stringValue = [NSString stringWithUTF8String:charlieReturn];