This is a bug in clang.
He should work
ARC is compatible with variable argument lists. If this is not the case, you will get an error from the compiler.
value is a strong reference, while the result of va_arg(_arguments, NSString *) is an unsafe unreachable reference: you can write va_arg(_arguments, __unsafed_unretained NSString *) and get the same compiled assembly, but if you try with a different owner qualifier, you will get compiler error because it is not supported.
So, while storing the value in value and assuming that the variable is actually used, the compiler should issue an objc_retain call and balance it with the objc_release call when this variable is destroyed. According to the report, the second call is emitted, while the first is missing.
This crashes with the string returned by stringWithDate: (and only that), because it is the only string that is not a constant. All other parameters are constant lines generated by the compiler, which simply ignore any memory management method, since they are stored in memory until the executable is loaded.
So, we need to understand why the compiler releases a release without saving it accordingly. Since you are not doing manual memory management or cheating ownership rules by casting with __bridge_transfer or __bridge_retained , we can assume that the problem comes from the compiler.
Undefined behavior is not the cause
There are two reasons why the compiler can fix an invalid build. Either the code containing the undefined behavior, or there is an error in the compiler.
Undefined behavior occurs when your code tries to execute something that is not defined by the C standard: when the compiler encounters undefined, it has the right to do whatever it wants. undefined behavior leads to programs that may or may not be broken. In most cases, the problem occurs in the same place as the undefined behavior, but sometimes it may seem unrelated, because compilers expect that the undefined behavior will not happen and relies on this expectation to perform some optimizations.
So, let's see if your code contains undefined behavior. Yes, since the replaceTokensWithStrings: method can call va_start and return without va_end , which is called ( return nil inside the for loop). The C standard explicitly states (in section 7.15.1.3) that this behavior is undefined.
If we replace return nil with break , your code will now be valid. However, this does not solve the problem.
Break compiler
Now that we have eliminated all the other possible causes, we need to face reality. There is an error in clang. We can see this by doing a lot of subtle changes that create a valid compiled assembly:
- If we compile with
-O0 instead of -Os , it works. - If we compile with clang 4.1, it works.
- If we send a message to an object before the
if condition, it works. - If we replace
va_arg(_arguments, NSString *) with va_arg(_arguments, id) , it will work. I suggest you use this workaround.