问题
Having some issues with the ...
in ObjectiveC.
I\'m basically wrapping a method and want to accept a nil
terminated list and directly pass that same list to the method I am wrapping.
Here\'s what I have but it causes an EXC_BAD_ACCESS
crash. Inspecting the local vars, it appears when otherButtonTitles
is simply a NSString
when it is passed in with otherButtonTitles:@\"Foo\", nil]
+ (void)showWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:title
message:message
delegate:delegate
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:otherButtonTitles] autorelease];
[alert show];
}
How do I simply siphon from the argument incoming to the argument outgoing, preserving the exact same nil
terminated list?
回答1:
You can't do this, at least not in the way you're wanting to do it. What you want to do (pass on the variable arguments) requires having an initializer on UIAlertView
that accepts a va_list
. There isn't one. However, you can use the addButtonWithTitle:
method:
+ (void)showWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:title
message:message
delegate:delegate
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:nil] autorelease];
if (otherButtonTitles != nil) {
[alert addButtonWithTitle:otherButtonTitles];
va_list args;
va_start(args, otherButtonTitles);
NSString * title = nil;
while(title = va_arg(args,NSString*)) {
[alert addButtonWithTitle:title];
}
va_end(args);
}
[alert show];
}
This is, of course, very problem-specific. The real answer is "you can't implicitly pass on a variable argument list to a method/function that does not have a va_list
parameter". You must therefore find a way around the problem. In the example you gave, you wanted to make an alertView with the titles you passed in. Fortunately for you, the UIAlertView
class has a method that you can iteratively call to add buttons, and thereby achieve the same overall effect. If it did not have this method, you'd be out of luck.
The other really messy option would be to make it a variadic macro. A variadic macro looks like this:
#define SHOW_ALERT(title,msg,del,cancel,other,...) { \
UIAlertView *_alert = [[[UIAlertView alloc] initWithTitle:title message:msg delegate:del cancelButtonTitle:cancel otherButtonTitles:other, ##__VA_ARGS__] autorelease]; \
[_alert show]; \
}
However, even with the variadic macro approach, you'd still need a custom macro for each time you wanted to do this. It's not a very solid alternative.
回答2:
How about constructing an NSInvocation
object? Since arguments must be passed by pointer, you could pass the pointer to the nil-terminated list.
You could also iterate over the parameters using marg_list()
and construct a nil-terminated list yourself.
These are just simple suggestions; I haven't tried them out.
回答3:
This is specific to the OP's UIAlertView
-wrapping case, and tested only on iOS7: It appears that once a UIAlertView
has been initialised with otherButtons:nil
, and then has its style set to UIAlertViewStylePlainTextInput
it doesn't call its delegate's alertViewShouldEnableFirstOtherButton:
to validate input. I'm not sure if this is a bug or intended behaviour but it broke my principle of least astonishment. This is reproducible with the following (I'll assume the delegate's alertViewShouldEnableFirstOtherButton:
is implemented):
UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Title"
message:@"message"
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:nil];
[av setAlertViewStyle:UIAlertViewStylePlainTextInput];
[av addButtonWithTitle:@"OK"];
[av show];
The solution, since UIAlertView happily accepts otherButtons:nil
, is to initialise UIAlertView
with otherButtonTitles (which can be nil), and iterate over the variadic arguments, as above:
+ (void)showWithTitle:(NSString *)title
message:(NSString *)message
delegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ...
{
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:title
message:message
delegate:delegate
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:otherButtonTitles] autorelease];
// add your [alert setAlertViewStyle:UIAlertViewStylePlainTextInput] etc. as required here
if (otherButtonTitles != nil) {
va_list args;
va_start(args, otherButtonTitles);
NSString * title = nil;
while(title = va_arg(args,NSString*)) {
[alert addButtonWithTitle:title];
}
va_end(args);
}
[alert show];
}
来源:https://stackoverflow.com/questions/2345196/objective-c-passing-around-nil-terminated-argument-lists