Attribute introspection to get a variable in Objective-C - objective-c

Given a variable id x and a string NSString *s how can I get the instance attribute with name s for variable x?
ie. If we write NSString *s=#"a", then we want x.a

The Objective-C Runtime Reference lists
Ivar class_getInstanceVariable(Class cls, const char * name)
which returns an opaque type representing an instance variable in a class. You then pass that to
id object_getIvar(id object, Ivar ivar)
to get the actual instance variable. So you could say
#import <objc/runtime.h>
id getInstanceVariable(id x, NSString * s)
{
Ivar ivar = class_getInstanceVariable([x class], [s UTF8String]);
return object_getIvar(x, ivar);
}
if the instance variable is an object. However, if the instance variable is not an object, call
Ivar object_getInstanceVariable(id obj, const char * name, void ** outValue)
passing in a pointer to a variable of the right type. For example, if the instance variable is an int,
int num;
object_getInstanceVariable(x, [s UTF8String], (void**)&num);
will set num to the value of the integer instance variable.

Providing that x is key-value coding compliant for the a property, you can just do this:
id result = [x valueForKey:s]

Related

Why does a string pointer in Objective-C accept and return the value of the string and not a memory address?

For example in this code:
NSString *greeting = #"Hello";
NSLog(#"Greeting message: %#\n", greeting );
Greeting takes the value of a string, not an address. It also displays a string in NSLog and not an address. However, I thought pointers were supposed to be used like this:
int var = 20; /* actual variable declaration */
int *ip; /* pointer variable declaration */
ip = &var; /* store address of var in pointer variable*/
NSLog(#"Address of var variable: %x\n", &var );
/* address stored in pointer variable */
NSLog(#"Address stored in ip variable: %x\n", ip );
/* access the value using the pointer */
NSLog(#"Value of *ip variable: %d\n", *ip );
return 0;
I've always wondered why it's okay to do this with string pointers.
Well, that is something called Syntactic Sugar. What we are actually seeing; exactly doesn't happen like that under the hood.
For example, the code you have written:
NSString *greeting = #"Hello";
NSLog(#"Greeting message: %#\n", greeting );
When you pass greeting into NSLog, actually the following line of code gets executed.
NSLog(#"Greeting message: %#\n", [greeting description]); // description is a method defined in NSObject and NSString inherits it.
And even if you do:
NSString *greeting = #"Hello";
Now, greeting variable doesn't hold the contents of the string, neither it can because it is a pointer. It just holds the address of NSString #"Hello" where it is stored. And again, the assignment of pointer happens under the hood. The same is the case with the C language; we can write the following code in C, and it will compile without any errors:
char *string = "Hello, world!";
In C, the string "Hello, world!" is basically a character array, and string variable actually stores the pointer to this character array.
If you see the definition of NSLog method, it looks something like this:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
It clearly shows that NSLog message receives an NSString pointer. But what do we actually pass? We pass the NSString in it, but what is actually passed is a pointer to that NSString, again under the hood :)
I hope this helps you.
%# is the string formatter for NSObjects, calling the objects -description method. If you want the pointer address of the string object try %p.
NSString *string = #"A string";
NSLog(#"Object contents: %#", string);
NSLog(#"Object address: %p", string);

Is a variable like "int nameofvariable" an object?

My question is as it is in the title. However, if it isn't an object, how can it then be maintained in NSMutableArray as an object? Because NSMutableArray is only for storing objects or am I wrong? :)
Primitive types, like int, must be wrapped in an object before it can be added to a collection class like NSMutableArray. Try this:
int nameOfVariable = 42;
NSMutableArray *array = ... // the initialized array
[array addObject:#(nameOfVariable)];
The last line is modern syntax that essentially means:
[array addObject:[NSNumber numberWithInt:nameOfVariable]];
Later on, when you need to get the value back, you do:
int someVariable = [array[someIndex] intValue];
No, it is not an object and you can therefore not put it in an NSArray. You have to wrap it in an NSNumber, which is an object and can be put in an NSArray.
An int is a primitive type inherited from C (examples of other primitive types are char, long, float, etc.). These are not Objective-C objects. To add a primitive type to an NSMutableArray or any other Objective-C collection, you must wrap them in an object. For number types like int, you would use NSNumber as follows:
int i = 1;
[array addObject:#(i)]; // #(i) is equivalent to [NSNumber numberWithInt:i]

Why dereferencing a NSString pointer is not necessary?

In the example
NSString *message = #"Hello";
message = #"World";
If message is just a pointer why don't I need to explicitly say whatever is in message is now equal to string or *message = #"World"; like in C?
DISCLAIMER
The discussion below gives a general idea on why you never dereferenciate a pointer to an object in Objective-C.
However, concerning the specific case of NSString literals, this is not what's happening in reality. While the structure described below is still sound and it may work that way, what's actually happening is that the space for a string literal is allocated at compile time, and you get its address back. This is true for string literals, since they are immutable and constant. For the sake of efficiency therefore each literal is allocated only once.
As a matter of fact
NSString * a = #"Hello";
NSString * b = #"Hello";
NSLog(#"%# %p", a, a); // Hello 0x1f4958
NSLog(#"%# %p", b, b); // Hello 0x1f4958
ORIGINAL ANSWER
Because it will be translated to
message = [[NSString alloc] initWithUTF8String:"Hello"]];
which will boil down to
message = objc_msgSend(objc_msgSend(objc_getClass("NSString"), #selector(alloc)), #selector(initWithUTF8String:), "Hello");
Now if we take a look to the signature of objc_msgSend
id objc_msgSend(id theReceiver, SEL theSelector, ...)
we see that the method returns an id type, which in Objective-C is the object type. But how is id actually defined?
typedef struct objc_object {
Class isa;
} *id;
id is defined as a pointer to an objc_object struct.
So in the end #"string" will translate in a function call that will produce a pointer to an object (i.e. an objc_object struct, if you prefer), which is exactly what you need to assign to message.
Bottom line, you assign pointers, not objects.
To better clarify the last concept consider this
NSMutableString * a = [NSMutableString stringWithString:#"hello"];
NSMutableString * b = a;
[a setString:#"hola"];
NSLog(#"%#", a); // "hola"
NSLog(#"%#", b); // "hola"
If you were assigning objects, b would have been a copy of a and any further modification of a wouldn't have affected b.
Instead what you get is a and b being two pointers to the same object in the heap.

incorrect variable value outside main()

i have this code
#import <Foundation/Foundation.h>
int testint;
NSString *teststring;
int Test()
{
NSLog(#"%d",testint);
NSLog(#"%#",teststring);
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
testint = 5;
NSString *teststring = [[NSString alloc] initWithString:#"test string"];
Test();
[pool drain];
return 0;
}
in output i have:
5
(null)
why Test function doesn't see correct teststring value? What should I do, to have correct "test string" in output?
You're shadowing a global variable with a local one. If the intent is to use the global testString, you shouldn't re-declare it with "NSString*".
in output i have:
5 (null)
why Test function doesn't see correct teststring value?
Because you never assigned anything there. In main, you declared a local variable with the same name, and initialized that variable with the pointer to the NSString object you created.
how should i declare global objects with "alloc init"?
You don't.
Declarations create variables (or sometimes types). The NSString *teststring lines (both of them) are declarations: One of a global variable, the other of a local variable.
alloc messages (and most other messages to classes) create objects.
Thus, this line:
NSString *teststring = [[NSString alloc] initWithString:#"test string"];
declared a local variable (teststring) and created a string object, and initialized the variable to hold the pointer to the string object.
(Note that “initWithString:” initializes the object, not the variable. The part from the = until the semicolon is the initializer for the variable.)
You meant to assign to the global variable, not declare a local variable. So, do that: Leave out the type specifier to turn the declaration into an assignment statement:
teststring = [[NSString alloc] initWithString:#"test string"];
By the way, you don't need to use alloc and initWithString: here. #"test string" is already an NSString object. And when you do alloc something, don't forget to release it (assuming you didn't turn on the GC).
You have two different variables named testint. The one in main() is shadowing the global one.
how should i declare global objects with "alloc init"?
Strings are a special case. You can do this:
NSString* foo = #"bar";

How to get the value of a variable based on it's string name?

I want to get the value of all ivars in an Objective-C class and load them into an array.
I have the following code:
Class class = [self class];
NSString *myClassName = NSStringFromClass([self class]);
unsigned int count;
Ivar *ivars = class_copyIvarList(class, &count);
for (int i=0; i<count; i++) {
Ivar ivar = ivars[i];
const char* name = ivar_getName(ivar);
const char* typeEncoding = ivar_getTypeEncoding(ivar);
NSLog(#"Class Name: %# ivar: %s Type Encoding: %s",myClassName, name, typeEncoding);
}
So I have the name as a string (name) and now I need the value of that ivar.
Something like:
value = [self valueFromStringName(name)];
So that if I have a date called myDate I can pass the string myDate into a method and it would return the date value it holds.
I've see routines that run methods from the string name of the method, but haven't see where you can get the value from a string name.
Given you are already using runtime functions why not continue and use:
id object_getIvar(id object, Ivar ivar)
to obtain the value of ivar?
If you really wish to use a string look up KVC, "Accessor Search Patterns for Simple Attributes", and valueForKey: on NSObject.
HTH