How can I convert a variable name into a string?
Example:
From this:
NSString *someVariable
int otherVariable
I want to get a NSString with the actual name of the variable, no matter what type it is.
So, for the two variables above I would want to get their names (someVariable, otherVariable).
I managed to solve my problem with this code snippet:
Import the objc runtime
#import <objc/runtime.h>
and you can enumerate the properties with:
- (NSArray *)allProperties
{
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *rv = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++)
{
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[rv addObject:name];
}
free(properties);
return rv;
}
Hope it helps someone.
Just add " ... " around the variable name. i.e.
"someVariable"
"otherVariable"
to get the string (as a const char*.) If you want an NSString*, use
#"someVariable"
#"otherVariable"
Within a macro, you can use the construction #... to put the quote ... unquote around a macro variable, e.g.
#define MyLog(var) NSLog(#"%s=%#", #var, var)
so that
MyLog(foo);
is expanded to
NSLog(#"%s=%#", "foo", foo);
These are C declarations, and C does not have the introspection capability to give you what you want.
You could probably write a preprocessor macro that would both declare a variable and also declare and initialize a second variable with the name of the first.
But this begs the question of why you need this level of introspection at all.
Related
I'm trying to learn objective-c and have encountered a warning in this example:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableDictionary *booklisting = [NSMutableDictionary dictionary];
int count; // < Am getting 'unused variable' warning here
[booklisting setObject:#"Wind in the Willows" forKey:#"100-432112"];
[booklisting setObject:#"Tale of Two Cities" forKey:#"200-532874"];
[booklisting setObject:#"Sense and Sensibility" forKey:#"200-546549"];
[booklisting setObject:#"Shutter Island" forKey:#"104-109834"];
NSLog(#"Number of books in dictionary = %lu", [booklisting count]);
would anyone know why?.. Would appreciate help..thanks
you are not using any where in your code.that's why warning comes like this.
int count;// count is an variable and
[booklisting count]//here count is a property of NSArray reference class
remove int count; and check it.
you arent using count... [booklisting count] is accessing a method of booklisting which is a NSMutableDictionary which has a method called count which returns how many entries are in the dictionary.
its coincidence that they have the same name.
put this line
count =[booklisting count];
before NSLog
or remove this int count; line
count is getter method for array.
Just remove int count; since you have not used variable count.
int count; // < Am getting 'unused variable' warning here
You declare a variable count.But it is never used.Hence the error is shown
Say I have my class
#interface Person : NSObject { NSString *name; }
I need to get the name of NSString's within my class
Person *person = [[Person alloc] init];
NSLog(#"Name of variable %s\n", _NameofVariable_(person->name));
Thanks for the answers, here's the solution I came up from the replies
//returns nil if property is not found
-(NSString *)propertyName:(id)property {
unsigned int numIvars = 0;
NSString *key=nil;
Ivar * ivars = class_copyIvarList([self class], &numIvars);
for(int i = 0; i < numIvars; i++) {
Ivar thisIvar = ivars[i];
if ((object_getIvar(self, thisIvar) == property)) {
key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];
break;
}
}
free(ivars);
return key;
}
As easy as
#define VariableName(arg) (#""#arg)
Then you do:
NSObject *obj;
NSString *str = VariableName(obj);
NSLog(#"STR %#", str);//obj
You can get the names of a class's instance variables with the Objective-C runtime API function class_copyIvarList. However, this is rather involved, rarely done and almost never the best way to accomplish something. If you have a more specific goal in mind than mere curiosity, it might be a good idea to ask about how to accomplish it in Objective-C.
Also, incidentally, person.name doesn't specify an instance variable in Objective-C — it's a property call. The instance variable would be person->name.
You might use preprocessor stringification and a bit of string twiddling:
NSUInteger lastIndexAfter(NSUInteger start, NSString *sub, NSString *str) {
NSRange found = [str rangeOfString:sub options:NSBackwardsSearch];
if(found.location != NSNotFound) {
NSUInteger newStart = NSMaxRange(found);
if(newStart > start)
return newStart;
}
return start;
}
NSString *lastMember(NSString *fullName) {
if(!fullName) return nil;
NSUInteger start = 0;
start = lastIndexAfter(start, #".", fullName);
start = lastIndexAfter(start, #"->", fullName);
return [fullName substringFromIndex: start];
}
#define NSStringify(v) (##v)
#define _NameofVariable_(v) lastMember(NSStringify(v))
If the person object is exposed as a property of the class, you can use objc_msgSend to get the value.
So, if you could access person using
[object person]
You could also do
objc_msgSend(object, "person")
For more details on message sending, including how to pass arguments to methods, see the Objective-C Runtime Programming Guide section on Messaging
The following works as a macro:
#define STRINGIZE(x) #x
Is it possible to get an array of all of an object's properties in Objective C? Basically, what I want to do is something like this:
- (void)save {
NSArray *propertyArray = [self propertyNames];
for (NSString *propertyName in propertyArray) {
[self doSomethingCoolWithValue:[self valueForKey:propertyName]];
}
}
Is this possible? It seems like it should be, but I can't figure out what method my propertyNames up there should be.
I did some more digging, and found what I wanted in the Objective-C Runtime Programming Guide. Here's how I've implemented the what I wanted to do in my original question, drawing heavily from Apple's sample code:
#import <Foundation/NSObjCRuntime.h>
#import <objc/runtime.h>
- (void)save {
id currentClass = [self class];
NSString *propertyName;
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(currentClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
propertyName = [NSString stringWithCString:property_getName(property)];
[self doSomethingCoolWithValue:[self valueForKey:propertyName]];
}
}
I hope this will help someone else looking for a way to access the names of an object's properties programatically.
dont forget
free(properties);
after the loop or you will get a leak. The apple documentation is clear:
An array of pointers of type objc_property_t describing the properties
declared by the class. Any properties declared by superclasses are not
included. The array contains *outCount pointers followed by a NULL
terminator. You must free the array with free().
If I have a class, how can I list all its instance variable names?
eg:
#interface MyClass : NSObject {
int myInt;
NSString* myString;
NSMutableArray* myArray;
}
I would like to get "myInt", "myString", and "myArray". Is there some way to perhaps get an array of names that I can iterate over?
I've tried searching the Objective-C documentation but couldn't find anything (and I'm not sure what this is called either).
As mentioned, you can use the Objective-C runtime API to retrieve the instance variable names:
unsigned int varCount;
Ivar *vars = class_copyIvarList([MyClass class], &varCount);
for (int i = 0; i < varCount; i++) {
Ivar var = vars[i];
const char* name = ivar_getName(var);
const char* typeEncoding = ivar_getTypeEncoding(var);
// do what you wish with the name and type here
}
free(vars);
#import <objc/runtime.h>
NSUInteger count;
Ivar *vars = class_copyIvarList([self class], &count);
for (NSUInteger i=0; i<count; i++) {
Ivar var = vars[i];
NSLog(#"%s %s", ivar_getName(var), ivar_getTypeEncoding(var));
}
free(vars);
Consider gen_bridge_metadata, which is intended for a completely different purpose, but can produce XML files from Objective-C header files.
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