va_args() causing EXC_BAD_ACCESS - ios

I'm getting a EXC_BAD_ACCESS when using va_args (iOS 7, Xcode 5.1.1, ARC on):
// ...
int val = sqlIntQuery(#"format_string", #"arg1"); // <-- does not work
int val = sqlIntQuery(#"format_string", #"arg1", nil); // <-- this works
// ...
- (int)sqlIntQuery:(NSString *)format, ...
{
va_list args;
va_start(args,format);
__unsafe_unretained id eachObject;
NSMutableArray *arguments = [NSMutableArray array];
while ( (eachObject = va_arg(args, id)) != nil ) { // <-- crash on 2nd loop
[arguments addObject:eachObject];
}
va_end(args);
// ... process 'arguments'
return 5; // return a computed intValue
}
If I put a "break;" at the end of the loop (because I only have one argument), or add "nil" as a last argument, there's no crash, but I don't think I should have to add the "nil". I suspect an ARC issue, but I'm using __unsafe_unretained, as suggested elsewhere on SO. (Is there a way I can push "nil" into to args?)
What's causing the failure on the second time through the loop?
EDIT Aug 6: My Solution:
The accepted solution by maddy pushed me in the right direction when he mentioned "the number of format specifiers." My format argument has a '?' placeholder for each argument, so I just count those. So, for the record:
- (int)sqlIntQuery:(NSString *)format, ...
{
int numberOfArgs = [format componentsSeparatedByString:#"?"].count - 1; // <<-- this solved my problem
va_list args;
va_start(args,format);
NSMutableArray *arguments = [NSMutableArray array];
while ( numberOfArgs-- ) {
id eachObject = va_arg(args, id);
[arguments addObject:eachObject];
}
va_end(args);
FMResultSet *rs = [db executeQuery:format withArgumentsInArray:arguments];
[rs next];
int ret = [rs intForColumnIndex:0];
[rs close];
return ret;
}
It's a double-wrapper. My routine is a wrapper around FMDB, which is itself a wrapper for SQLite.

You need the nil or some other way to know how many arguments to grab. va_list has no magic way to know when to stop.
Things like stringWithFormat: don't need a nil because it determines the number of arguments based on the number of format specifiers (which is why they need to match or your code goes boom). Note how methods like NSDictionary dictionaryWithObjectsAndKeys: requires a nil terminator or UIAlertView initWithTitle... needs the nil terminator for the otherButtonTitles parameter.
What you could do is make use of the following NSString method:
- (int)sqlIntQuery:(NSString *)format, ... {
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
// do whatever
va_end(args);
return 5;
}
Of course this solutions assumes you wish to build a string from format and the variable arguments to your method.
If you really do need to populate an array then you will need to pass a nil terminator when you call your sqlIntQuery method.

Related

iOS Objective C Completion Block will not return data where required from Azure Mobile Apps Framework method. Building Bundle Application

I have been struggling with this for hours, maybe days. I need to return the results of a query into a string and have my method return the string. However, the query has to be executed with a method within MicrosoftAzure's Mobile App Sync Framework. I cannot change this method to include changes that allow me to pass the data back into a local variable string. So I'm stuck with the querywithpredicate: method only allowing void returned and all the data is sent to the completion block. I've found many block examples and tried them but none will work with the predefined method from the Azure Framework. I've also seen many examples of the blocks using data but NSLogging all my results from an nspredicate query is great for debugging but not real useful. Truly I'm stuck between a rock and a hard place because I also don't control the calling method to my azureQuery method.
A Third Party Calls -> azureQuery: (myMethod) -> azureQuery calls itemTable readWithPredicate: method in the Azure Framework.
The third party caller is waiting for the query results.
How can I return the results by allowing the block to finish its work on the query? Also keep in mind, I am building a bundle, so I may not have access to the AppDelegate Singleton Class of all iOS Apps in the future. I nee a way for azureQuery to return the data that I need from the NSPredicate query.
Things I've already tried:
1) _block NSString *data => This Failed never populated
2) created a global variable that both my method and the completion block could access => this Failed.
3) Created a while loop to check for different variables to not be empty. Loop never ended => This Failed.
4) Tried creating a block return result in my method declaration of azureQuery. This didn't achieve the desired results. It left me in another block in another method.
I need to escape the block at some point in time to store results in a NSString that can be used without block notation.
-(NSString*)azureQuery:(NSString *)tableName {
// Query Param
NSString *str = [NSString stringWithFormat:#"2017-07-06T03:56:38.569+00:00"];
// convert to date
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'+00:00'"];
NSDate *dte = [dateFormat dateFromString:str];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'+00:00'"];
str = [dateFormat stringFromDate:dte];
dte = [dateFormat dateFromString:str];
NSLog(#"Date to use after formating: %#", dte);
// Create a predicate that finds items where complete is false
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"createdAt > %#", dte];
QSTodoService* serviceAzure = [[QSTodoService alloc] init];
MSTable *itemTable = [serviceAzure.client tableWithName:tableName];
__block NSString *myData;//maybe this will work
//begin query method
[itemTable readWithPredicate:predicate completion:^(MSQueryResult * _Nullable result, NSError * _Nullable error) {
//actions inside the completion block
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
if(error) {
NSLog(#"ERROR %#", error);
} else {
for(NSDictionary *item in result.items) {
NSString *value = [item objectForKey:#"mbsq"];
[stringArray addObject:value];
}
}
//results of query in string
NSString * result2 = [stringArray componentsJoinedByString:#"\r"];
self.fName = result2;
}];//end of completion block
while(_fName.length == 0) {
//buy me some time for completion block to populate fName Global Variable
}
NSLog(#"Did it populate:%#",self.fName);
return myData;
}
Thanks for the help.
Update: I've tried to use a completion handler block but no good.
When I get back to the calling method, I run into the same problem with the completion block finishing after the return errorcode is sent. The results.SetAsText ( *outText2, *default_locale ); never fires in time.
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
errorResult = 0;
dispatch_async(dispatch_get_main_queue(), ^{
//FMScripterViewController* cmd = [[FMScripterViewController alloc] init];
//[cmd azureQuery:tableName];
FMScripterViewController* cmd = [[FMScripterViewController alloc] init];
[cmd azureQuery:tableName withResponseCallback:^(NSString *responseDictionary) {
//NSLog(#"test in plugin responses %#", responseDictionary);
fmx::TextUniquePtr outText2;
//const char *c = [rtnString UTF8String];
const std::string bar([responseDictionary UTF8String]);
std::cout << "Firehouse:" << bar;
outText2->Assign ( bar.c_str(), fmx::Text::kEncoding_UTF8 );
std::cout << "outText2:" << &outText2;
fmx::LocaleUniquePtr default_locale;
results.SetAsText ( *outText2, *default_locale );
//results.SetAsText( *result_text, dat1.GetLocale() );
}];
});
endif
errorResult = 0;
return errorResult;
}

how to pass the uncertain parameters form one method to another on Objective-C? [duplicate]

i have googled and came to know that how to use the variable arguments. but i want to pass my variable arguments to another method. i m getting errors. how to do that ?
-(void) aMethod:(NSString *) a, ... {
[self anotherMethod:a];
// i m doing this but getting error. how to pass complete vararg to anotherMethod
}
AFAIK ObjectiveC (just like C and C++) do not provide you with a syntax that allows what you directly have in mind.
The usual workaround is to create two versions of a function. One that may be called directly using ... and another one called by others functions passing the parameters in form of a va_list.
..
[obj aMethod:#"test this %d parameter", 1337);
[obj anotherMethod:#"test that %d parameter", 666);
..
-(void) aMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a withParameters:(va_list)valist
{
NSLog([[[NSString alloc] initWithFormat:a arguments:valist] autorelease]);
}
You cannot pass variadic arguments directly. But some of these methods provide an alternative that you can pass a va_list argument e.g.
#include <stdarg.h>
-(void)printFormat:(NSString*)format, ... {
// Won't work:
// NSString* str = [NSString stringWithFormat:format];
va_list vl;
va_start(vl, format);
NSString* str = [[[NSString alloc] initWithFormat:format arguments:vl] autorelease];
va_end(vl);
printf("%s", [str UTF8String]);
}
Have you considered setting up your arguments in either an array or dictionary, and coding conditionally?
-(void) aMethodWithArguments:(NSArray *)arguments {
for (id *object in arguments) {
if ([object isKindOfClass:fooClass]) {
//handler for objects that are foo
[self anotherMethod:object];
}
if ([object isKindOfClass:barClass]) {
//and so on...
[self yetAnotherMethod:object];
}
}
}
I think you could use macros to achieve same thing.
Let's say you wanna pass aMethod's variable arguments to another
-(void) aMethod:(NSString *) a, ... {
}
You could define your another 'method' using macro though it is not a real method:
#define anotherMethod(_a_,...) [self aMethod:_a_,##__VA_ARGS__]
This is my solution.

Ways to build NSStrings in Objective-C? [duplicate]

C# has syntax that allows you to specify the argument index in a string format specifier, e.g.:
string message = string.Format("Hello, {0}. You are {1} years old. How does it feel to be {1}?", name, age);
You can use arguments more than once and also omit arguments that are provided from being used. Another question mentions the same formatting for C/C++ in the form of %[index]$[format], e.g. %1$i. I have not been able to get NSString to fully respect this syntax, because it does behave well when omitting arguments from the format. The following does not work as expected (EXC_BAD_ACCESS because it attempts to dereference the age parameter as a NSObject*):
int age = 23;
NSString * name = #"Joe";
NSString * message = [NSString stringWithFormat:#"Age: %2$i", name, age];
The positional arguments are respected only if there are no missing arguments from the format (which seems to be an odd requirement):
NSString * message = [NSString stringWithFormat:#"Age: %2$i; Name: %1$#", name, age];
All these calls work properly in OS X:
printf("Age: %2$i", [name UTF8String], age);
printf("Age: %2$i %1$s", [name UTF8String], age);
Is there a way of accomplishing this using NSString in Objective-C / Cocoa? It would be useful for localization purposes.
NSString and CFString support reorderable/positional arguments.
NSString *string = [NSString stringWithFormat: #"Second arg: %2$#, First arg %1$#", #"<1111>", #"<22222>"];
NSLog(#"String = %#", string);
Also, see the documentation at Apple: String Resources
The following code fixes the bug specified in this issue. It is a workaround and renumbers the placeholders to fill gaps.
+ (id)stringWithFormat:(NSString *)format arguments:(NSArray*) arguments
{
NSMutableArray *filteredArguments = [[NSMutableArray alloc] initWithCapacity:arguments.count];
NSMutableString *correctedFormat = [[NSMutableString alloc ] initWithString:format];
NSString *placeHolderFormat = #"%%%d$";
int actualPlaceholderIndex = 1;
for (int i = 1; i <= arguments.count; ++i) {
NSString *placeHolder = [[NSString alloc] initWithFormat:placeHolderFormat, i];
if ([format rangeOfString:placeHolder].location != NSNotFound) {
[filteredArguments addObject:[arguments objectAtIndex:i - 1]];
if (actualPlaceholderIndex != i) {
NSString *replacementPlaceHolder = [[NSString alloc] initWithFormat:placeHolderFormat, actualPlaceholderIndex];
[correctedFormat replaceAllOccurrencesOfString:placeHolder withString:replacementPlaceHolder];
[replacementPlaceHolder release];
}
actualPlaceholderIndex++;
}
[placeHolder release];
}
if (filteredArguments.count == 0) {
//No numbered arguments found: just copy the original arguments. Mixing of unnumbered and numbered arguments is not supported.
[filteredArguments setArray:arguments];
}
NSString* result;
if (filteredArguments.count == 0) {
//Still no arguments: don't use initWithFormat in this case because it will crash: just return the format string
result = [NSString stringWithString:format];
} else {
char *argList = (char *)malloc(sizeof(NSString *) * [filteredArguments count]);
[filteredArguments getObjects:(id *)argList];
result = [[[NSString alloc] initWithFormat:correctedFormat arguments:argList] autorelease];
free(argList);
}
[filteredArguments release];
[correctedFormat release];
return result;
}
After doing more research, it appears Cocoa respects positional syntax in printf. Therefore an alternate pattern would be:
char msg[512] = {0};
NSString * format = #"Age %2$i, Name: %1$s"; // loaded from resource in practice
sprintf(msg, [format UTF8String], [name UTF8String], age);
NSString * message = [NSString stringWithCString:msg encoding:NSUTF8StringEncoding];
However, it would be nice if there was an implementation of this on NSString.

String formatting, varargs and EXEC_BAD_ACCESS

I have two methods in my class called TestClient
-(void)log:(NSString *)logMessage, ...
{
va_list ap;
va_start(ap, logMessage);
[self log:logMessage withParameters:ap];
va_end(ap);
}
-(void)log:(NSString *)logMessage withParameters:(va_list)valist
{
NSString *formattedString = [[NSString alloc] initWithFormat:logMessage arguments:valist]; //Crashes here
[self callMethod:#"log" withParams:formattedString, nil]; //Calls my method.
}
Here is my unit test:
-(void)testWtfCondition
{
int test = 1;
NSString *test2 = #"wtf";
[proxy log:#"This is a test: %# %#",test, test2];
}
My unit test crashes at the NSString formattedString line with EXEC_BAD_ACCESS. Am I doing something wrong with string formatting or varargs here? Is it because I'm trying to do a format with an int?
%i (or %d) - if you want to print integers
[proxy log:#"This is a test: %i %#",test, test2];
%# - will call [description] on class you want to print. For build-in variable types like float, int you need to cannot use it, since they are not objects.
For more string format, you can check out String Programming Guide.

EXC_BAD_ACCESS and stringByAppendingString

Getting EXC_BAD_ACCESS when setting a label with the value returned by the following function defined in a class called DataHelper (all database handling done here):
+(NSString *)getCurrentBalanceAndType:(NSString *)account
{
sqlite3_stmt *statement=NULL;
float bal=0;
NSString *type=NULL, *balance_type=NULL;
//String decimalFormat="%.2f";
statement = [DataHelper getDetailsFromAccountBal:account:[DataHelper currentMonth]];
if (sqlite3_step(statement) == SQLITE_ROW)
{
bal = sqlite3_column_double(statement,2);
type = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
}
sqlite3_finalize(statement);
NSString *stringBal=[NSString stringWithFormat:#"%.2f", bal];
type=[[type uppercaseString] stringByAppendingString:#"r"];
balance_type=[[stringBal stringByAppendingString:#" "]stringByAppendingString:type];
[type release];
return balance_type;
}
This is how I am setting the label in viewDidLoad:
lbCreditCurrBal.text=[DataHelper getCurrentBalanceAndType:#"Some Text"];
Please help.
You are overreleasing type. Remove [type release];. And use ARC to avoid this kind of errors.
When you call a method that begins with anything except init or copy, you’re returned an object that will be autoreleased at some point in the future.
reference- ray's memory management blog
keeping above in mind I think you should not release the type object as said by Nikolai.

Resources