I'm trying to understend construction like this:
- (void)someMethodWithArgs:(type?) param, ...
{
???
}
[self someMethodWithArgs:arg1, arg2, arg3];
How to get acces to arguments list?
Should 'type' be a pointer, or it can be 'int', for example?
The way Objective-C functions work.
Declaration
- (void) someMethodWithArgA:(type)paramName argB:(typeB)paramNameB
{
// do something with paramName and paramNameB
}
Calling
[self someMethodWithArgA:val argB:valB];
The C-equivalent would be :
void someMethodWithArgs(type paramName, typeB paramNameB)
{
// do something with paramName and paramNameB
}
someMethodWithArgs(val,valB);
And of course, as with C, variable types can be anything (why should they be just pointers?).
A simple example :
- (int)addNum:(int)a withNum:(int)b
{
int c = a+b;
return c;
}
int k = [self addNum:2 withNum:3];
// k = 5
Reference
The Objective-C model of object-oriented programming is based on
message passing to object instances. In Objective-C one does not
simply call a method; one sends a message.
http://en.wikipedia.org/wiki/Objective-C#Messages
UPDATE
Implementing method with variable number of arguments
#import <Cocoa/Cocoa.h>
#interface NSMutableArray (variadicMethodExample)
- (void) appendObjects:(id) firstObject, ...; // This method takes a nil-terminated list of objects.
#end
#implementation NSMutableArray (variadicMethodExample)
- (void) appendObjects:(id) firstObject, ...
{
id eachObject;
va_list argumentList;
if (firstObject) // The first argument isn't part of the varargs list,
{ // so we'll handle it separately.
[self addObject: firstObject];
va_start(argumentList, firstObject);
// Start scanning for arguments after firstObject.
// As many times as we can get an argument of type "id"
while (eachObject = va_arg(argumentList, id))
[self addObject: eachObject];
va_end(argumentList);
}
}
#end
From : http://developer.apple.com/library/mac/#qa/qa1405/_index.html
In ObjC argument list syntax originates C argument list syntax.
- (void) appendObjects:(id) firstObject, ...
{
id eachObject;
va_list argumentList;
va_start(argumentList, firstObject); // Start scanning for arguments after firstObject.
while (eachObject = va_arg(argumentList, id)) // As many times as we can get an argument of type "id"
[self addObject: eachObject]; // that isn't nil, add it to self's contents.
va_end(argumentList);
}
You can find more information from here: developer.apple.com
Update: ooops, i'm a little late :)
Matt Gallagher provides a nice tutorial on Variable Arguments lists:
Variable argument lists in Cocoa
In Apple's docs there's also a short Technical Q&A QA1405 on this topic:
Variable arguments in Objective-C methods
Related
I created an objective-c method which will invoke a method via NSInvocation:
typedef void (^ScriptingEmptyBlock)();
typedef void (^ScriptingErrorBlock)(NSError *error);
- (void)scripting_execute:(NSString *)operation withParams:(nullable NSArray *)args {
SEL selector = [self scripting_selectorForOperation:operation];
Class class = [self class];
NSMethodSignature *signature = [class instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setTarget:self];
for (int idx = 0; idx < args.count; idx ++) {
id arg = args[idx];
[invocation setArgument:&arg atIndex:idx + 2];
}
ScriptingEmptyBlock success = args[1];
// Breakpoint added on next line to test for nil
success(); // this is nil and would crash!
// (lldb) po args.count
// 3
// (lldb) po success
// Printing description of success:
// (ScriptingEmptyBlock) success = 0x0000000000000000
// (lldb) po args[1]
// (Function)
//[invocation getArgument:&success atIndex:2]; // also tried this and got nil as well
[invocation invoke];
}
The method takes an "operation" which is translated into a selector by overriding scripting_selectorForOperation: in subclasses and then performs the invocation.
All of that works, except when the invoked method has block arguments they are nil, I added the test for nil I describe with comments, when attempting to read the closure from the array it will be nil.
Called like:
let successClosure: ScriptingEmptyBlock = {
print("Renamed product")
}
let errorClosure: ScriptingErrorBlock = { error in
print("Failed to rename product: \(error)")
}
let params:[Any] = [ "testName", successClosure, errorClosure]
object.scripting_execute (ScriptOperation.updateProductName.rawValue, withParams: params)
Why is closure becoming nil?
success is not nil (in fact, NSArray cannot contain nils). If you print it like NSLog(#"%#", success);, it will say (Function), not (null). And if you print its class like NSLog(#"%#", [success class]);, it will say _SwiftValue. Basically, it is a Swift value that is bridged into Objective-C.
The problem is that the object success points to is not an Objective-C block. It is a Swift closure, and Swift closures are not the same as Objective-C blocks. Trying to use invoke it as if it were an Objective-C block causes undefined behavior. po in the debugger prints it wrong probably because it is printing it assuming it were type ScriptingEmptyBlock (a block type). If you do po (id) success, it will print (Function).
As to how you can explicitly put an Objective-C block into the array from Swift, the only way I figured out to do it something like:
let params:[Any] = [ "testName",
successClosure as (#convention(block) () -> Void)!,
errorClosure as (#convention(block) (NSError) -> Void)!]
object.scripting_execute (ScriptOperation.updateProductName.rawValue,
withParams: params)
I am not sure why it's necessary to put the function type inside a !, but it doesn't seem to work otherwise. Maybe someone else can find a better way.
I must admit that I don't fully understand why this is happening, but as far as I can tell this has nothing to do with using NSInvocation and would happen even if we just passed a Swift closure to an Objective-C function via a parameter of type id. Passing an Objective-C block via id works just fine, not sure why: Swift closures are supposed to be compatible with Objective-C blocks. As you know, elements of NSArray are of type id, so any Objective-C object can be an array element.
To work around this problem of accessing a Swift closure passed via id in Objective-C one can introduce a wrapper class.
// In a header:
#interface EmptyBlockWrapper : NSObject
#property EmptyBlock _blk;
#end
// In an implementation file (just an empty implementation):
#implementation EmptyBlockWrapper
#end
Then we can use a wrapper instance instead of a block as an array element in Swift:
let myBlock : EmptyBlock = {
print("In Swift EmptyBlock...")
}
let myBlockWrapper = EmptyBlockWrapper()
myBlockWrapper._blk = myBlock
In an Objective-C method we can call it as follows, assuming args is NSArray *:
EmptyBlockWrapper * emptyBlockWrapper = args[1];
emptyBlockWrapper._blk();
Hopefully this is helpful. Of course, this is just a simplified example to give you an idea; this could be made much fancier.
I need to write an Objective-C method whose input is an array of strings and whose output is that list of strings concatenated, but only those which are not nil.
Any ideas if this function is already somewhere in the foundation?
If it's not already done, how can I write a method which has an unlimited number of NSString parameters? For instance, like the params keyword in C# which will essentially convert the params list into an array.
thanks
You need ... (ellipsis):
- yourMessage:arg1, ...;
Here's official Apple information on the subject.
Here I am copying their sample:
#import <Cocoa/Cocoa.h>
#interface NSMutableArray (variadicMethodExample)
- (void) appendObjects:(id) firstObject, ...; // This method takes a nil-terminated list of objects.
#end
#implementation NSMutableArray (variadicMethodExample)
- (void) appendObjects:(id) firstObject, ...
{
id eachObject;
va_list argumentList;
if (firstObject) // The first argument isn't part of the varargs list,
{ // so we'll handle it separately.
[self addObject: firstObject];
va_start(argumentList, firstObject); // Start scanning for arguments after firstObject.
while (eachObject = va_arg(argumentList, id)) // As many times as we can get an argument of type "id"
[self addObject: eachObject]; // that isn't nil, add it to self's contents.
va_end(argumentList);
}
}
#end
Variable arguments in Objective-C methods
Your answer might just be in this nice post.
http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
The function [NSArray arrayWithObjects:foo, bar, nil] passes a nil-terminted list of string to a function.
If I want to write a similar function, what does the declaration look like, and how do I iterate through the strings?
I quote http://developer.apple.com/mac/library/qa/qa2005/qa1405.html, which contains the full truth.
Here's an example of an Objective-C category, containing a variadic method that appends
all the objects in a nil-terminated list of arguments to an NSMutableArray instance:
#import <Cocoa/Cocoa.h>
#interface NSMutableArray (variadicMethodExample)
- (void) appendObjects:(id) firstObject, ...; // This method takes a nil-terminated list of objects.
#end
#implementation NSMutableArray (variadicMethodExample)
- (void) appendObjects:(id) firstObject, ...
{
id eachObject;
va_list argumentList;
if (firstObject) // The first argument isn't part of the varargs list,
{ // so we'll handle it separately.
[self addObject: firstObject];
va_start(argumentList, firstObject); // Start scanning for arguments after firstObject.
while (eachObject = va_arg(argumentList, id)) // As many times as we can get an argument of type "id"
[self addObject: eachObject]; // that isn't nil, add it to self's contents.
va_end(argumentList);
}
}
#end
I'm not sure if Obj-c has its own particular support for this, but in C you do:
function nArgs(int n, ...)
To support multiple args, and the following style of implementation:
{
va_list ap;
va_start(ap, n);
while (n-- > 0) {
int i = va_arg(ap, int);
}
va_end(ap);
}
Here for more info: http://www.cprogramming.com/tutorial/lesson17.html
NSArray declares that method like this:
+ (id)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
When you implement it, you'll need to use the va_arg macros to get at each argument.
Look up va_list, va_start(), va_arg(), etc.
More info can be seen in the man page for va_arg and by looking at stdarg.h
Alright, so I think I'm doing this the right way. I'm new to objective-C, so I'm not sure about the syntax... I have a set of code that I need to call multiple times, from different files. So I made a new class that has a method in it that I'll call and pass it the values that it needs.
Because I am passing different values I've put them in a dictionary and decided to just pass the dictionary. Here is that code:
NSNumber *testNum = [NSNumber numberWithInt:varMoney];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:#"OMG, Object 1!!!!" forKey:#"1"];
[dictionary setObject:#"Number two!" forKey:#"2"];
[dictionary setObject:testNum forKey:#"3"];
This code creates a test variable, and then puts it into the dictionary "dictionary." That all works, I have my nice little dictionary. However, now I need to create the class and it's method that will recieve the dictionary, and do something with it.
This is my class header file:
#import <UIKit/UIKit.h>
#interface EndOfTurnObjC : UIView {
}
#end
And this is the implementation file:
#import "EndOfTurnObjC.h"
#implementation EndOfTurnObjC
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
I haven't created any of the real code, because I'm not sure how to do the passing. I need to create a function (Method?) in the class that will take a Dictionary has a parameter, and then return the dictionary.
I also have no idea how to call such a function because it's in the class. So, the questions are:
1: How do I define the method in the class to accept the dictionary as a parameter (and then perhaps some example code to pull out one of the objects in a dictionary, so I can be sure it works)
2: How do I return the dictionary at the end of the method?
3: How do I call this method, in the class, from another class? (I know it involves making an object of thing class and calling the method of the object... I think, but I'm not sure about the syntax.)
Please include relavent code for the 3 files (header, implementation, and the other class that I call from). Thank you so much, I've been working on this particular problem for a while now.
Apple's The Objective-C Programming Language is a good and pretty concise reference for Objective-C syntax. What you want is just a normal method that takes an NSDictionary as a parameter. So as given in that document:
A message with a single argument affixes a colon (:) to the selector name and puts the argument right after the colon. This construct is called a keyword; a keyword ends with a colon, and an argument follows the colon, as shown in this example:
[myRectangle setWidth:20.0];
So a method call to pass dictionary would look like:
[someObject setAttributes:dictionary];
In the header:
-(NSMutableDictionary *) doSomethingWithDictionary:(NSMutableDictionary *) aDict;
in the implementation:
-(NSMutableDictionary *) doSomethingWithDictionary:(NSMutableDictionary *) aDict{
//do something with the dictionary
return aDict;
}
To call the method:
NSMutableDictionary *returnDict=[EndOfTurnObjC doSomethingWithDictionary:dictionary];
Note that as a matter of good design you wouldn't want to pass a mutable dictionary around like a token. That is asking for trouble. Instead pass static dictionaries and get another dictionary back.
You also shouldn't be passing data to a UIView. Instead, your UIViewController should process the data and then populate the view's UI elements as needed.
if you just want to do stuff to your dictionary u just
-(void) changeMyDictionary:(NSMutableDictionary * ) dictionary_
{
[dictionary_ doStuff];
....
...
}
no need to return the dictionary.
I asked a similar question, but I couldn't get it working exactly. I'm building an iPhone app, and there is a method that I want called from different files. I figured the easiest way would simply be to make a method in another file, and call the method from the other files.
Here are some problems. I need to return multiple values from the method, after passing it multiple values. For example, I'm passing it: (int, int, int, string, string). And it needs to return all of those values, after they have been changed. Someone showed me this code:
- (NSDictionary *)EndOfTurn:(int)varTurns withFatness:(int)varFatness
{
varTurns--;
if (varTurns <= 0) {
varFatness = varFatness - 5;
}
else {
varFatness += 2;
}
return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:varFatness], #"FATNESS", [NSNumber numberWithInt:varTurns], #"TURNS", nil];
}
However, this code doesn't work, and I need some more information to really understand it. Let's assuming I'm passing it these values:
int varMoney;
int varNumSheep;
int varNumShepherds;
NSString *test1;
NSString *test2;
So I need to get all of these values back from the method.
How do I declare this in the header file? This should be in an Objective-C file, but could you give me the code for the entire file so I can see where it would go with the #implementation and #end, whatnot. Also, how would I call this method?
What about passing in the values as pointers?
For example:
- (void) getValuesForInt:(int *)int1 anotherInt:(int *)int2 aBool:(BOOL *)bool1 anotherBool:(BOOL *)bool2 {
if (*int1 == 42 && *int2 == 0) {
*int1 = 0;
*int2 = 42;
}
if (*bool1 == NO) {
*bool2 = YES;
}
}
Then you can invoke it like:
int int1 = 42;
int int2 = 0;
BOOL bool1 = NO;
BOOL bool2 = NO;
[self getValuesForInt:&int1 anotherInt:&int2 aBool:&bool1 anotherBool:&bool2];
NSLog(#"int1: %d int2: %d bool1: %d bool2: %d", int1, int2, bool1, bool2);
//prints "int1: 0 int2: 42 bool1: 0 bool2: 1"
Edit:
This works equally well with objects. You'll often see this used when dealing with NSError objects:
NSError *error = nil;
[anObject doSomething:foo error:&error];
Can be implemented as:
- (void) doSomething:(id)terrible error:(NSError **)error {
if ([terrible isEqual:reallyBad]) {
if (error != nil) { *error = [NSError errorWithDomain:#"domain" code:42 userInfo:nil]; }
}
}
You can use a block closure to pass back multiple values from a method like this. -rrh
[self heyFunctionGiveMeBackTwoValuesFromThisFruitArray:#[#"apple", #"orange", #"banana", #"apple"] findThisFruit:#"apple" closureFunction:^(int fruitCount, NSString* fruitString)
{
NSLog(#"Two values returned, int-fruitCount:%d, NSString-fruiteString:%#", fruitCount, fruitString);
}];
- (void)heyFunctionGiveMeBackTwoValuesFromThisFruitArray:(NSArray*)fruitsArray findThisFruit:(NSString*)findThisFruit closureFunction:(void (^)(int fruitCount, NSString *fruitString))passBackResultsUsingThisClosure
{
NSInteger fruitsFound = 0;
NSString* fruitsMessage = [NSString stringWithFormat:#"No %# Found", findThisFruit];
for (NSString* string in fruitsArray)
{
if ([string compare:findThisFruit] == NSOrderedSame)
{
fruitsFound++;
}
}
if (fruitsFound > 0)
{
fruitsMessage = [NSString stringWithFormat:#"You have %# on your list this many times:%d", findThisFruit, fruitsFound];
}
passBackResultsUsingThisClosure(fruitsFound, fruitsMessage);
}
Results:
Two values returned, int-fruitCount:2, NSString-fruiteString:You have apple on your list this many times:2
If you have that many different things that need to be returned from a method, either encapsulate it into an NSDictionary as others have suggested or consider just defining a class. You can declare the instance variables and properties to encapsulate the data, as needed.
Defining a class to encapsulate such information proves to be quite efficient and maximizes flexibility. If you need to refactor your app such that the collection of data gains new fields, needs to be saved for later, or might need to gain functionality, a class will ease these changes.
Since you can only return a single value from any method in C and C-derived languages, you simply need to return a single value that represents all of your other values. This is what your sample code is doing with an NSDictionary.
The sample code is correct, even if it's a bit contrary to common Objective-C style.
What you declare in the header file is simply the declaration of the method, that is:
#interface MyClass : NSObject
- (NSDictionary *)EndOfTurn:(int)varTurns withFatness:(int)varFatness;
#end
In the source file, then:
#implementation MyClass
// code, as given above
#end
If you only need to return primitive values, then returning a struct may be the optimal solution. You get compile-time error checking (e.g. as opposed to an NSDictionary where you could attempt to read an invalid key), while not requiring all the code/files involved in creating a class.
typedef struct myStruct {
int varMoney;
int varNumSheep;
int varNumShepherds;
} myStruct;
Apple uses structs in many of their methods too (e.g. CGPoint, CGRect).
The reason this won't work with objects is because ARC forbids this.
One slight improvement to the last point in some designs is to use a struct holding enum members. This gives you the compile-time checking already mentioned, something that looks like an object in the return value, and the benefit of clear cases if you need to check the values in the return.
The struct:
typedef struct _SIXRecorderStateChange {
SIXRecorderState oldState;
SIXRecorderState newState;
} SIXRecorderStateChange;
The client code:
SIXRecorderStateChange stateChange = [recorderState stop];
if (stateChange.newState == SIXRecorderStopped) {
...
...