Objective-C--Parse NSString to a callable method - objective-c

Could I parse an NSString to have it call a method that matches its name in Objective-C? Here is an example:
If I had a string called doSomething, and I had a method called -(void) doSomething, could I do something like scanf to parse whatever text I typed in to check if there were any method matching it, and if yes then call that method?

Try NSSelectorFromString(#"methodName"); and the associated NSObject methods like respondsToSelector:

jxpx777's answer will give you the information you were looking for, but in case you want more, the runtime has a long list of C functions that provide a fairly complete introspection of objects and classes.
For instance, if you want an NSArray of method names implemented by a class, you can do something like this:
Class myClass = [self class];
unsigned int methodCount;
Method *methods = class_copyMethodList(myClass, &methodCount);
NSMutableArray *methodNames = [NSMutableArray arrayWithCapacity:10];
for (int i = 0; i < methodCount; i++) {
const char *methodNameCStr = sel_getName(method_getName(methods[i]));
NSString *methName = [NSString stringWithCString:methodNameCStr
encoding:NSASCIIStringEncoding];
[methodNames addObject:methName];
}
free(methods);
NSLog(#"Methods: %#", methodNames);
You will notice that plain C calls and Objective-C/Cocoa are mixed freely.

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);

How can I pass a C array to a objective-C function?

I'm not familiar with C. How can I pass a C array to a Objective-C function ?
I actually need an example of a class function converting NSArray to C arrays.
This is what I have so far:
+ (NSArray *)convertArray:(NSString*)array { //I don't think this is correct: the argument is just a NSString parameter and not an array
NSMutableArray * targetArray = [NSMutableArray array];
for (i = 0; i < SIZE; i++) //SIZE: I dunno how to get the size of a C array.
{
[targetArray addObject: [NSString stringWithString:array[i]];
}
return targetArray;
}
There are a few ways.
If your array size is fixed at compile-time, you can use the C99 static modifier:
-(void) doSomething:(NSString *[static 10]) arg
{
}
If not, you have to pass it as two separate arguments. One as a pointer to the first element of it, and the second as the length of it:
-(void) doSomething:(NSString **) arg count:(size_t) count
{
}
Now you can access your variables like any other array you may have.
Because you are dealing with a C-array of objective-c objects, you can actually use NSArray's built in constructor for turning a C-array into a NSArray:
NSArray *result = [NSArray arrayWithObjects:arg count:count];

Objective c implement method which takes array of arguments

Hee
Does anybody know how to implement an method in objective c that will take an array of arguments as parameter such as:
[NSArray arrayWithObjects:#"A",#"B",nil];
The method declaration for this method is:
+ (id)arrayWithObjects:(id)firstObj...
I can't seem to make such method on my own. I did the following:
+ (void) doSometing:(id)string manyTimes:(NSInteger)numberOfTimes;
[SomeClass doSometing:#"A",#"B",nil manyTimes:2];
It will give the warningtoo many arguments to function 'doSometing:manyTimes:'
Thanks already.
The ellipsis (...) is inherited from C; you can use it only as the final argument in a call (and you've missed out the relevant comma in your example). So in your case you'd probably want:
+ (void)doSomethingToObjects:(id)firstObject, ...;
or, if you want the count to be explicit and can think of a way of phrasing it well:
+ (void)doManyTimes:(NSInteger)numberOfTimes somethingToObjects:(id)firstObject, ...;
You can then use the normal C methods for dealing with ellipses, which reside in stdarg.h. There's a quick documentation of those here, example usage would be:
+ (void)doSomethingToObjects:(id)firstObject, ...
{
id object;
va_list argumentList;
va_start(argumentList, firstObject);
object = firstObject;
while(1)
{
if(!object) break; // we're using 'nil' as a list terminator
[self doSomethingToObject:object];
object = va_arg(argumentList, id);
}
va_end(argumentList);
}
EDIT: additions, in response to comments. You can't pass the various things handed to you in an ellipsis to another function that takes an ellipsis due to the way that C handles function calling (which is inherited by Objective-C, albeit not obviously so). Instead you tend to pass the va_list. E.g.
+ (NSString *)doThis:(SEL)selector makeStringOfThat:(NSString *)format, ...
{
// do this
[self performSelector:selector];
// make string of that...
// get the argument list
va_list argumentList;
va_start(argumentList, format);
// pass it verbatim to a suitable method provided by NSString
NSString *string = [[NSString alloc] initWithFormat:format arguments:argumentList];
// clean up
va_end(argumentList);
// and return, as per the synthetic example
return [string autorelease];
}
Multiple arguments (also known as an arglist) can only come at the end of a method declaration. Your doSomething method would look something like this:
+ (void)doNumberOfTimes:(NSInteger)numberOfTimes withStrings:(id)firstArg, ...
{
va_list args;
va_start(args, firstArg);
NSString * argString = firstArg;
while (argString != nil)
{
// do something with argString here
argString = va_arg(args, NSString *);
}
va_end(args);
}
To be called as follows:
[SomeClass doNumberOfTimes:2 withStrings:#"A", #"B", nil];
See also: How to create variable argument methods in Objective-C
I think you're after a variadic function. Here's Apple's documentation: http://developer.apple.com/library/mac/qa/qa2005/qa1405.html

Anyway to get string from variable name?

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

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