NSLog - How to print object name? - ios

Consider,
NSString *myString = #"Welcome";
NSLog(#"%#",myString);
will print Welcome in console.
Can I print the string like "myString: Welcome"?
I mean, can I get the object name("myString") along with object value("Welcome")?

Use the following code:
#define stringVariable(x) NSLog( #"%s:%#",#x, x)
NSString *myString=#"Welcome";
stringVariable(myString);
Note: The general principle is that when you put a # in front of an argument within the body of a #define, the preprocessor replaces it with a C string of the exact expression passed to the macro. When you pass a variable name, you'll get that name.

Related

Get the data stored in address using the * operator

int i = 17;
int *addressOfI = &i;
printf("the int stored at addressOfI is %d\n", *addressOfI);
The question is: If I can get the data stored in addressOfI using the * operator, why it doesn't works for type NSString? like following:
NSString *string = #"Hello world!"
printf("the NSString stored at string is %#\n", *string);
why it doesn't works for type NSString?
Because NSString is an Objective-C object and not a primitive type. The NSString * pointer actually points to a struct objc_object which provides the framework for the object system. You can probably "see" some primitive types within this framework (i.e. members of objc_object) however it's supposed to be a black box to normal developers.
The actual reason your second piece of code will crash is that the %# format specifier expects to call the description method on the object you pass in as an argument and you have dereferenced that object pointer so it's no longer a valid object pointer.

Xcode, ios, Set name of variable to value of another variable?

I want to set the name of a variable to the value of another variable are there any other ways to do this because I don't think this is the way.
NSString *myint = #"a";
NSString *([NSString stringWithFormat:#"%#", myint]) = #"something";
NSLog(#"%#", a);
No, you can't do that. Once your code is compiled, your variables don't really have names -- just locations. The names you see in the debugger are provided by a symbol file which the debugger uses to map locations to names.
Key-value coding could help, depending on what you're really trying to accomplish. With KVC, you use key names to refer to values rather than accessing variables, much as you do with a dictionary. For example, if you have an object with properties foo and bar, you can then do something like this:
NSString *key = #"foo";
[myObject setValue:#(5) forKey:key];
You could even override -setValue:forKey: so that it accepts any key and remembers the value (which is exactly what a dictionary does).
You can go in the other direction (set a variable to the name of another variable) using the stringification operator, but it's kinda hacky and not usually all that useful. In a nutshell, macro parameters prefixed with a # are used as literal strings instead of being evaluated. So you'd create a macro like this:
#define string(x) #x
and then you'd use it somewhere in your code like this:
int foo = 5;
NSLog("The name of the variable is %s and its value is %d.", string(foo), foo);
with the following result:
The name of the variable is foo and its value is 5.
I agree with Paulw11. You could define a NSMutableDictionary to hold all your variables by key. I don't think there is any way the compiler can use a handle determined at runtime. But you can achieve the same affect.
So say, for instance, that both the variable handle and value were NSString, then you could do something like this.
NSMutableDictionary *myObjects = [[NSMutableDictionary dictionary] init];
NSString variableName = #"myString";
[myObjects setObject #"Variable value" forKey: variableName];
NSLog("Variable %# has value %#.", variableName, [myObjects objectForKey: variableName]);

Customized NSLog to print all type of objects

I'm trying to extend NSLog that can print any type of object. Means, my custom class gets the value that need to be printed from the user and checks its class type and then it will print as desired format.
Consider my extended class name is, MyLog (So it contains MyLog.h and MyLog.m)
MyLog.h:
void MyLog (id objectValue);
MyLog.m:
#import "MyLog.h"
void MyLog (id objectValue)
{
if ([objectValue isKindOfClass:[NSString class]])
{
NSLog(#"%#",objectValue); //Same for NSArray, NSDictionary, NSMutableArray, NSMutableDictionary
}
else if ([objectValue isKindOfClass:[NSData class]])
{
NSLog(#"%#",[[NSString alloc]initWithData:objectValue encoding:NSUTF8StringEncoding]);
}
....
....
}
So, if I include this class (MyLog.h) in prefix file, I can call the below method from any class to simply print the given object.
MyLog(valueOfObject);
Problems:
The CGRect, CGpoint, CGSize related things (which are not an object) can not be passed as id to MyLog() function.
I tried to print the object name along with its value for more readability. Refer this post.
For example,
NSString *myString = #"Welcome";
MyLog(myString);
This will print myString: Welcome instead of just printing Welcome.
I can achieve this my defining a preprocessor like
#define MYLOG(x) NSLog( #"%s:%#",#x, x)
So, I tried to customize it in the following way
#define MYLOG(x) NSLog(#"%s:%#",#x, MyLog(x))
But it throws "Argument type 'void' is incomplete" error.
Questions:
How can I pass non objects as id to a function?
How can I call a function within a preprocessor macro?
Help needed!! Just Confused!!
If you implement -(NSString*)description you can print any object in NSLog with NSLog(#"%#", myObject).
So just add -(NSString*)description in the classes of objects that you want to print out, and in them just return the NSString value that you think is relevant for that object.

basic programming concept: when to initialize new string versus just creating new variable

So I've been programming for a year but this concept still trips me up sometimes. My understanding is that if you don't initialize and allocate a new object when you create a new variable name using the pointer operator '*' that the danger is that the value of that new variable will always just be tied to whatever memory address it is you've pointed the name. For example, in #2 if string is set to '6' because array[1] is set to '6' but later the value of element #1 in array changes to '7' then string will return 7. But if I used method 1 where I use a string class method to allocate and initialize a memory address of its own for string then string would stay '6' even if later element #1 is changed to hold a value of '7'. Is this right?
What’s the difference between:
NSString *string = [NSString stringwithstring: array[1]];
AND
NSString *string = array[1];
As a sidenote: I have a tough time understanding how this will matter much because if array is immutable then the only way it could be changed is if a new array is initialized and reallocated with a different value for element #1. Also, once my view controller gets popped off of the stack when the user continues to navigate through my app, if it gets called again all of these objects will get recreated from scratch- so it usually won't matter. But I just want to make sure I am getting the concept anyways.
Actually, whether you use the 1st or 2nd option has nothing to do with the array itself.
The 2nd option would not result in any change even if the array was mutable and you replaced the object at index 1. string would still point to the original object.
In the example you've given, the choice of the two options only matters if in reality, the string you get from the array is an NSMutableString. If the string is an immutable NSString then either option gives the same result. But if you actually have a mutable NSMutableString, then option 2 means that your string value can change over time of another reference to the mutable string makes changes to the string.
Example:
NSMutableString *mutable = [NSMutableString stringWithString:#"hello"];
NSArray *array = #[ #"stuff", mutable ];
NSString *string1 = [NSString stringWithString:array[1]];
NSString *string2 = array[1];
NSLog(#"string1 = %#, string2 = %#", string1, string2);
[mutable appendString:#" there"];
NSLog(#"string1 = %#, string2 = %#", string1, string2);
The log output will be:
string1 = hello, string2 = hello
string1 = hello, string2 = hello there
See how string2 was changed as a result of modifying mutable.
(edit: per martin R's comment--suppose the strings in the example are mutable strings)
I made a diagram to help explain what's going on.
The first diagram is your initial setup. string = nil and you have a string reference in array[1].
1) do string = array[1]. Now string and array[1] point to the same string object.
2a) alternately, as in 2a, you can do string = [ NSString stringWithString:array[1]]... This will point string to a copy of the string in array[1]
Notice in all cases if you mutate the array, string still contains a reference to either a) the original string from array[1] or the copy you created.
2b) For example, let's do array[1] = #"test". [string description] will still return ABC
HTH
Your string pointer does not remember where the string was at the time you assigned it, so array-level changes don't matter.
The practical difference is about what happens if your array actually contains mutable strings instead of plain ones. Your first example creates a new string that has the content that's at array[1] when the assignment takes place. In your second example, the thing pointed to by string could be different moment-to-moment if some other code changed the underlying mutable string that's in array[1] at assignment time.

Poor compiler warnings

how to get rid of the compiler warning in XCode 5.1
in Stringtable is of course a string with a format specifier (updated: now in front of code)
"fmtDetail" = "Count: %d";
int number = 0;
//Compiler warning: Data argument not used by format string
NSString *text = [NSString stringWithFormat:NSLocalizedString(#"fmtDetail", nil), number];
//this gets no warning
NSString *fmtDetail = NSLocalizedString(#"fmtDetail", nil);
NSString *text2 = [NSString stringWithFormat:fmtDetail, number];
It is not the compiler warning that is poor - you should correct your code.
There seems to be an %d (or similar) missing in the #"fmDetail".
Or you should get rid of the number argument - that is not used.
Depends on what you are actually trying to do...
NSString *text = [NSString stringWithFormat:NSLocalizedString(#"fmtDetail%d", nil), number];
NSString *text = [NSString stringWithFormat:NSLocalizedString(#"fmtDetail %d", nil), number];
NSString *text = [NSString stringWithString:NSLocalizedString(#"fmtDetail", nil)];
Second note: this #"fmtDetail%d" should match the key in the plist dictionary (translated strings). It could also be simly #"theDeatils" - the string that returned from your plist is the one that should actually hold formatting data for the string.
Why would one want to use the %d in the key? Because NSLocalizedString returns the key as the result if it doesn't find string with appropriate key.
EDIT: MartinR found the real reason for why this warning appears. Just a note that might be useful: since localizing strings usually means translation into many languages (duh) you might need to use numbered placeholders - not all languages share the same basic sentence structure.
This seems to be not a bug, but a new feature of the compiler that comes with Xcode 5.1 (beta). It expects now that in
[NSString stringWithFormat:NSLocalizedString(key, ...), arguments... ]
the key itself is a valid format for the given arguments.
(In other words, the key uses the same format specifiers as its value from the strings file).
For example:
// Source code:
[NSString stringWithFormat:NSLocalizedString(#"Count = %d", nil), number]
// Localizable.strings:
"Count = %d" = "Die Anzahl ist %d";
This is an advantage because the compiler can now check that the number and types
of the format specifiers match the actual arguments even with localizable format
strings. That was not possible before (as far as I know).
For example, this will cause a warning in Xcode 5.1 beta, but not in Xcode 5.0.2:
[NSString stringWithFormat:NSLocalizedString(#"fmtDetail %f", nil), 13];
// warning: format specifies type 'double' but the argument has type 'int' [-Wformat]
(And as #rokjarc already had pointed out, using a valid format string as key makes
sense anyway, because NSLocalizedString() returns the key if no matching string
is found in the Localizable.strings file.)

Resources