简体   繁体   中英

ARC and varargs causes BAD_ACCESS

I am going through a list of varargs and when I reach the last object in the list I always get a BAD_ACCESS error. I strongly assume that it is an ARC related issue.

This is what I do:

-(NSString *)replaceTokensWithStrings:(NSString *)firstKey, ... {

        va_list _arguments;
        va_start(_arguments, firstKey);

        for (NSString *_currentArgument = firstKey; _currentArgument != nil; _currentArgument = va_arg(_arguments, NSString*)) {
            NSLog(@"%@", _currentArgument);
        }

        va_end(_arguments);

    return nil;
}

I have to somehow tell the compiler to retain the result returned by va_arg but I can't seem to figure out where and how to apply this paradigm.

Update 1: This is how I call my method:

[@"Hello <firstname> <lastname>" replaceTokensWithStrings:
  @"firstname", @"Peter",
  @"lastname", "Smith", 
  nil];

Update 2: I updated my question and added an error I made to reveal what I've actually done wrong. See my answer below.

I was thoroughly on the wrong path. The problem was not the function at all but how I called the function (The other participants could not have known this because I did not do the error in my example in my question).

I have updated my question and the actual problem was that I did not pass a list of NSStrings but 5 NSStrings and the last one was an normal C String.

The last C string caused the BAD_ACCESS :

BAD:

"Smith"

GOOD:

@"Smith"

Now I am embarassed :-)

Whenever you have a varargs list in C you need to somehow specify the number of items in the list or identify the last item -- the language does not do it for you. This can be via a format statement -- number of args matches number of format items -- or some other scheme. The default scheme is to end the parameter list in the calling statement with nil or NULL .

If you look at the definitions of things like NSArray arrayWithObjects , the list must always be terminated with nil .

I'm working with the ARC as well, I'm using the following pattern and it works fine for me, I've never experienced any issue with it, it might help you:

- (void)workingWithDictionaries:(NSDictionary *)dictionary, ... {

    va_list _arguments;
    va_start(_arguments, dictionary);

    for (NSDictionary *_currentArgument = dictionary; _currentArgument != nil; _currentArgument = va_arg(_arguments, NSDictionary*)) {
        NSLog(@"%@", _currentArgument);
    }

    va_end(_arguments);
}

I faced same issue today, and can tell you technically it has nothing to do with ARC or retain, but your method declaration/prototype should be

-(NSString *)replaceTokensWithStrings:(NSString *)firstKey, ... 
    NS_REQUIRES_NIL_TERMINATION;

That way you tell Xcode and C preprocessor to use the MACRO and asks always to finish with 'nil' so it stops va_arg() from accessing a wrong position (BAD_ACCESS)

It has nothing to do with ARC. va_arg does not check that it arrived at the end of the list, so will return a pointer to the memory block after the list, which can be anything. Hence the EXC_BAD_ACCESS. Generally you give the amount of arguments as extra parameter, something like:

-(void *)replaceTokensWithNumberOfStrings:(int)numStrings strings:(NSString *)str1, ... {

Then use the numStrings in your while loop.

If you are expecting an id type from a va_arg return (in this example, NSString*) and it returns a nil value, a crash will occur. This is because arc is trying to retain the nil that is returned.

to avoid this add __unsafe_unretained to the variable you are assigning to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM