Objective-C, Simple String input from Console? - objective-c

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

Related

Printing Current Method in Objective-C

How do I print the name of the method I am in, in Objective-C?
In Java/C# I could do this using reflection. Is similar possible with Objective-C?
For just printing method name, use
NSLog(#"%#", NSStringFromSelector(_cmd));
To printing method arguments is a complex task.
every message in ObjectiveC is converted to objMsgSend (id self, SEL, arg0...)
We need to walk through stack, and print them out, as we see after _cmd + sizeof(SEL) will give us address to arg0 (but the size and type for arg0 is unknown).
Method method = class_getInstanceMethod([self class], _cmd);
unsigned nargs = method_getNumberOfArguments(method);
void *start = _cmd;
for(unsigned i = 0; i< nargs ; i++) {
char *argtype = method_copyArgumentType(method, i);
//find arg size from argtype
// walk stack given arg zie
free(argtype);
}
Basic implementations should look like this.
So if the arguments are objects, that will be great (as we know the size of argument are 32/64 bit). Otherwise we need to do type encoding, and move cursor by argument's size. Have look https://github.com/holtwick/HOLog , that does exactly what you want, but it works under Simulator only.

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

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.

simple code with EXC_BAD_ACCESS

I am new to the objective c and i write the code according to a reference book.
but something went wrong and I don't know why.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
if (argc==1){
NSLog(#"you need to provide a file name");
return (1);
}
FILE *wordFile = fopen(argv[1], "r");
char word[100];
while(fgets(word , 100, wordFile)){
word[strlen(word)-1] = '\0';
NSLog(#"the length of the %s is %lu", word, strlen(word));
}
fclose(wordFile);
return 0;
}
the tool indicates that the while part went wrong, EXC_BAD_ACCESS.
Any idea?
It compiles and runs fine on my machine. But imagine you have an empty line in your file. Then strlen(word) will return zero. Hence word[strlen(word)-1] = '\0'; will try to set some memory which might not be valid since word[-1] might not be a valid memory cell, or a memory cell that you can legally access.
Oh, and by the way, it has nothing to do with objective-c. This is mostly (but for the NSLog call) pure ansi C.

How to output NSString Type in NSLog Function in Objective-C?

I want a input from user their name and output that input name in NSLog using NSString.
I don't know which % sign and how to output that.
Can i use scanf() function for that?
Please help me , i am just beginner of Objective-C.
You can use %# for all objects including NSString. This will in turn call the objects description method and print the appropriate string. Most objects have a rather useful representation already there (e.g. NSArray objects return the descriptions of all their contents).
Mark Dylan is the name which would be stored in the Name variable.
NSString* Name = #"Mark Dylan";
This code will allow you to ask their name and scan it into memory which will be stored in the Name variable.
NSLog(#"What is your name?");
scanf("%#", &Name);
If you want to print out the variable you can use;
NSLog(#"Your name is %#", Name);
%# is what you want. It fit for object like NSString, [YourViewController class]
To get input from the user use a UITextField or a NSTextField. To output a string to the log file you can use NSLog, ie:
NSString* userName = #"Zawmin";
NSLog(#"name = %#", userName);
NSLog accepts a format string, so you can do something like this:
#include <stdio.h>
#include <Foundation/Foundation.h>
// 1024 characters should be enough for a name.
// If you want something more flexible, you can use GNU readline:
// <http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html>
#define MAX_NAME_LENGTH 1024
// Get name from user input
char name[MAX_NAME_LENGTH];
name[0] = '\0'; // just in case fgets fails
fgets(name, MAX_NAME_LENGTH, stdin);
// Put name into NSString object and output it.
NSString *name = [NSString stringWithUTF8String:name];
NSLog(#"%#", name);
%# works for all Objective-C objects.
If you want to output a C-string (char* or const char*), use %s. Never put a non-literal string as the first argument to NSLog as this opens security holes.

Is string formation optimized by the compiler?

I was trying to answer another question about the == operator and I created this code:
NSString *aString = #"Hello";
NSString *bString = aString;
NSString *cString = #"Hello";
if (aString == bString)
NSLog(#"CHECK 1");
if (bString == cString)
NSLog(#"CHECK 2");
if ([aString isEqual:bString])
NSLog(#"CHECK 3");
if ([aString isEqual:cString])
NSLog(#"CHECK 4");
NSLog(#"%i", aString);
NSLog(#"%i", bString);
NSLog(#"%i", cString);
But was surprised at the results:
Equal[6599:10b] CHECK 1
Equal[6599:10b] CHECK 2
Equal[6599:10b] CHECK 3
Equal[6599:10b] CHECK 4
Equal[6599:10b] 8240
Equal[6599:10b] 8240
Equal[6599:10b] 8240
Is there some compiler trickery going on here?
There is clearly string uniquing going on, at least within a single compilation unit. I recommend you take a brief tour through man gcc during which you visit all uses of "string". You'll find a few options that are directly relevant to literal NSStrings and their toll-free-bridged counterparts, CFStrings:
-fconstant-string-class=class-name sets the name of the class used to instantiate #"..." literals. It defaults to NSConstantString unless you're using the GNU runtime. (If you don't know if you are, you aren't.)
-fconstant-cfstrings enables use of a builtin to create CFStrings when you write CFSTR(...).
You can disable uniquing for C string literals using -fwritable-strings, though this option is deprecated. I couldn't come up with a combination of options that would stop the uniquing of NSString literals in an Objective-C file. (Anyone want to speak to Pascal string literals?)
You see -fconstant-cfstrings coming into play in CFString.h's definition of the CFSTR() macro used to create CFString literals:
#ifdef __CONSTANT_CFSTRINGS__
#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
#else
#define CFSTR(cStr) __CFStringMakeConstantString("" cStr "")
#endif
If you look at the implementation of the non-builtin __CFStringMakeConstantString() in CFString.c, you'll see that the function does indeed perform uniquing using a very large CFMutableDictionary:
if ((result = (CFStringRef)CFDictionaryGetValue(constantStringTable, cStr))) {
__CFSpinUnlock(&_CFSTRLock);
}
// . . .
return result;
See also responses to the question, "What's the difference between a string constant and a string literal?"
NSString is defined as an immutable type, so whenever the compiler can optimize things by combining identical strings, it should. As your code demonstrates, gcc clearly does perform this optimization for simple cases.
Well for cString and aString, C,C++, and Objective C compilers can reuse a compile time string object if it is declared in more than one location.
Maybe simple copy-on-write optimization? As all 3 strings point to the same 'set of characters' there's not point in creating separate copies until you modify one of the strings.
Probably the characters are stored in static part of memory (with the code) and NSStrings* point to that part of memory. Once you attempt to modify one of the strings it will create new string somewhere else (heap) and then reference that memory.