I'm new to objective C. Usually in C# we can use params[] string as argument type when we want to send single or multiple strings to a method. Which we can either pass a single string or collection of strings without any data type issue. Do we have anything similar in objective C
All Objective-C reference type is a subclass of NSObject. This allow us some level of dynamic typing.
Below two versions are basically the same.
Old, generic version:
- (void)funcWithString:(id)param {
NSLog(#"funcWithString %#", param);
}
- (void)funcWithStringArray:(NSArray*)paramArr {
NSLog(#"funcWithStringArray %#", paramArr);
for (id str in paramArr) {
[self funcWithString:str];
}
}
More modern, static version:
- (void)funcWithString:(NSString*)param {
NSLog(#"funcWithString %#", param);
}
- (void)funcWithStringArray:(NSArray<NSString*>*)paramArr {
NSLog(#"funcWithStringArray %#", paramArr);
for (NSString* str in paramArr) {
[self funcWithString:str];
}
}
C# Object ~> Obj-C id
C# [] ~> Obj-C NSArray
Used like below (all 3 are equivalent):
[self funcWithString: #"single string"];
[self funcWithStringArray: #[#"single string"]];
[self funcWithStringArray: [NSArray arrayWithObject: #"single string"]];
In objective-c , if you are want to pass the single object where array of object is required, then you just have to declare the array with single object.
You cannot pass the single object where array is considered
In Objective-c Array is declared as simple
NSArray *array = [NSArray arrayWithObject:#"String1"];
Pass this array to function
Related
I'm currently working on a project where the user defines some parameters in a NSDictionnary, that I'm using to setup some objects.
For example, you can ask to create a Sound object with parameters param1=xxx, param2=yyy, gain=3.5 ... Then an Enemi object with parameters speed=10, active=YES, name=zzz ...
{
active = NO;
looping = YES;
soundList = "FINAL_PSS_imoverhere_all";
speed = 100.0;
}
I then instantiate my classes, and would like to set the ivars automatically from this dictionnary.
I've actually wrote some code to check that this parameter exists, but I'm having trouble in actually setting the parameter value, especially when the parameter is non object (float or bool).
Here's what I'm doing so far :
//aKey is the name of the ivar
for (NSString *aKey in [properties allKeys]){
//create the name of the setter function from the key (parameter -> setParameter)
NSString *setterName = [aKey stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[aKey substringToIndex:1] uppercaseString]];
setterName = [NSString stringWithFormat:#"set%#:",setterName];
SEL setterSelector = NSSelectorFromString(setterName);
//Check if the parameter exists
if ([pge_object respondsToSelector:setterSelector]){
//TODO : automatically set the parameter
}
else{
[[PSMessagesChecker sharedInstance]logMessage:[NSString stringWithFormat:#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]] inColor:#"red"];
NSLog(#"Cannot find %# on %#", aKey, [dict objectForKey:#"type"]);
}
}
}
As you can see, I don't know what to do once I've found that the parameter exists on the object. I tried to use "performSelector... withObject..., but my problem is that some of the parameters are non-objects (float or bool).
I also tried to get the class of the parameter, by using the setter, but it didn't help.
Did anyone manage to do something like that?
Jack Lawrence's comment is spot on.
What you are looking for is called Key Value Coding, or just KVC.
This fundamental part of Cocoa lets you get and set any instance variable using its name as a String and a new value.
It will automatically handle coercing Objects to primitive values, so you can use it for int and float properties too.
There is also support for validating values and handling unknown properties.
see the docs
your code, without validation, could be written
for( id eachKey in props ) {
[anOb setValue:props[eachKey] forKey:eachKey];
}
or just
[anOb setValuesForKeysWithDictionary:props];
as Jack said.
For the non-object parameters you have to put them into an object, for example NSNumber or NSValue. You can then add these objects into your dictionary.
For Example:
float f = 0.5;
NSNumber f_obj = [NSNumber numberWithFloat:f];
I have a class with an initializer that takes a NSDictionary:
-(id)initWithVector:(NSDictionary *) vectorDictionary;
when i try to pass it a NSDictionary, its giving me an error:
Incompatible point types sending'VectorClass * _strong' to parameter
type 'NSDictionary *'
code:
// myVectorList is an array of dictionaries
for (NSDictionary *vector in self.myFielder.myVectorList)
{
if ([vector isKindOfClass:[NSDictionary class]])
{
// hardcoded for testing purposes
if ([[vector objectForKey:HANDLE] isEqualToString:#"pt07p48u17aj75qx8n2fri9jlkrc262yt8"])
{
// GET THE WARNING ON PASSING "VECTOR"
VectorClass *vector = [[VectorClass alloc] initWithVector:vector];
[vector retrieveVectorAttributeTable];
[vector retrieveVectorMetadataTable];
}
}
}
if i typecast (NSDictionary *)vector, no warning.
vector should be a dictionary, so why am i getting the warning?
You use the same name for two different variables. The inner most variable is of type VectorClass defined in the same line, so the compiler tries to pass it to the init method, instead, change its name:
VectorClass *vectorC = [[VectorClass alloc] initWithVector:vector];
[vectorC retrieveVectorAttributeTable];
[vectorC retrieveVectorMetadataTable];
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];
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
I would like to pass a NSMutableArray by reference so that it can be altered by another method. What would be the correct syntax for this?
Thanks,
Objective-C objects are always passed by reference (using pointers) - you can't pass them by value.
I.e. the following is fine:
- (void)mutateArray:(NSMutableArray*)array {
// alter array ...
}
... and can be e.g. invoked like this:
NSMutableArray *array = ...;
[self mutateArray:array];
There is also the possibility of passing a pointer by reference:
- (void)newArray:(NSMutableArray **)array;
In that case array is used as an out-parameter - you pass a reference to a pointer to receive an instance:
- (void)newArray:(NSMutableArray **)array {
*array = [[NSMutableArray alloc] init];
}
... which could be called like so:
NSMutableArray *array = nil;
[self newArray:&array];
Using out-parameters is usually only seen if the return-value is already used and additional information has to be returned. An example would be error-information as dreamlax noted.
In addition to Georg Fritzche's answer, it may be worth noting that some methods expect to be given the address of an object pointer. For example:
NSError *anError; // points to garbage now
NSStringEncoding enc;
NSString *aString = [NSString stringWithContentsOfFile:#"/some/file.txt"
usedEncoding:&enc
error:&anError];
if (aString == nil)
{
// anError now points to an initialised NSError object.
}
It gets tricky because some documented methods require you to release objects obtained in this manner, and some don't (for an example of one that does require explicit releasing, see NSPropertyListSerialization).
As Georg Fritzsche said NSMutableArray passed be reference automatically, but not the NSArray. The best option is too look at the code bellow:
void mutateImmutableArray(NSArray *array);
void mutateMutableArray(NSMutableArray *array);
void mutateImmutableArrayByRef(NSArray **array);
void mutateMutableArrayByRef(NSMutableArray **array);
int main(int argc, const char * argv[]) {
#autoreleasepool {
//Change immutable array in method that expects immutable array
NSArray *immutable = #[#1,#2,#3];
mutateImmutableArray(immutable);
NSLog(#"After 1: %#",immutable); // 1,2,3
//Change mutable array in method that expects immutable array
NSMutableArray *mutable = [#[#1,#2,#3]mutableCopy];
mutateImmutableArray(mutable);
NSLog(#"After 2: %#",mutable); //1,2,3
//Change mutable array in method that expects mutable array
mutable = [#[#1,#2,#3]mutableCopy];
mutateMutableArray(mutable);
NSLog(#"After 3: %#",mutable); //1,2,3, Four
//Change immutable array in method that expects immutable array by reference
immutable = #[#1,#2,#3];
mutateImmutableArrayByRef(&immutable);
NSLog(#"After 4: %#",immutable); //4,5,6
//Change mutable array in method that expects mutable array by reference
mutable = [#[#1,#2,#3]mutableCopy];
mutateMutableArrayByRef(&mutable);
NSLog(#"After 5: %#",mutable); //1,2,3, Four
}
return 0;
}
void mutateImmutableArray(NSArray *array)
{
array = #[#4,#5,#6];
}
void mutateImmutableArrayByRef(NSArray **array)
{
*array = #[#4,#5,#6];
}
void mutateMutableArray(NSMutableArray *array)
{
[array addObject:#"Four"];
}
void mutateMutableArrayByRef(NSMutableArray **array)
{
[*array addObject:#"Four"];
}