I want to return an NSMutableString in my Foundation program. However, I get the following error:
warning: Semantic Issue: Incompatible pointer to integer conversion
returning 'NSMutableString *' from a function with result type 'int'
for the following code:
int main (int argc, const char * argv[])
{
NSMutableString* result = #"testing";
[pool drain];
return result;
}
That's your main function. As you can see from the declaration int main(), it returns an int. In fact, main() is only allowed to return an int, which indicates failure or success (generally 0 means success and any other number is a program-specific error code). You can't return anything else there — it's just part of the language. If you're trying to print the string, you can use NSLog(#"%#", result) or printf("%s", [result UTF8String]).
Related
I wrote a Helper class with c functions for an iOS Library with the following pattern.
There are 2 wrapping (variadic) functions, which finally call the same function, with slightly different parameter. Idea is to have "default" properties being set.
__attribute__((overloadable)) void func1(NSString* _Nonnull format, ...);
__attribute__((overloadable)) void func1(int param1, NSString* _Nonnull format, ...);
Both will then call the following function:
void prefixAndArguments(int param1, NSString* _Nonnull format, va_list arguments);
Implementation as followed:
__attribute__((overloadable)) void func1(NSString* _Nonnull format, ...)
{
va_list argList;
va_start(argList, format);
prefixAndArguments(0, format, argList);
va_end(argList);
}
__attribute__((overloadable)) void func1(int param1, NSString* _Nonnull format, ...)
{
va_list argList;
va_start(argList, format);
prefixAndArguments(param1, format, argList);
va_end(argList);
}
void prefixAndArguments(NMXLogLevelType logLevel, NSString* _Nullable logPrefix, __strong NSString* _Nonnull format, va_list arguments)
{
// Evaluate input parameters
if (format != nil && [format isKindOfClass:[NSString class]])
{
// Get a reference to the arguments that follow the format parameter
va_list argList;
va_copy(argList, arguments);
int argCount = 0;
NSLog(#"%d",argCount);
while (va_arg(argList, NSObject *))
{
argCount += 1;
}
NSLog(#"%d",argCount);
va_end(argList);
NSMutableString *s;
if (numSpecifiers > argCount)
{
// Perform format string argument substitution, reinstate %% escapes, then print
NSString *debugOutput = [[NSString alloc] initWithFormat:#"Error occured when logging: amount of arguments does not for to the defined format. Callstack:\n%#\n", [NSThread callStackSymbols]];
printf("%s\n", [debugOutput UTF8String]);
s = [[NSMutableString alloc] initWithString:format];
}
else
{
// Perform format string argument substitution, reinstate %% escapes, then print
va_copy(argList, arguments);
// This is were the EXC_BAD_ACCESS will occur!
// Error: Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
s = [[NSMutableString alloc] initWithFormat:format arguments:argList];
[s replaceOccurrencesOfString:#"%%"
withString:#"%%%%"
options:0
range:NSMakeRange(0, [s length])];
NSLog(#"%#",s);
va_end(argList);
}
...
}
My Unit Tests for the function look the following (order is important).
// .. some previous cases, I commented out
XCTAssertNoThrow(NMXLog(#"Simple string output"));
XCTAssertNoThrow(NMXLog(#"2 Placeholders. 0 Vars %# --- %#"));
The crash happens when I want to use the arguments and the format (making format strong did not solve the problem, and does not seem being part of the problem, see below):
s = [[NSMutableString alloc] initWithFormat:format arguments:argList];
Here is the Log:
xctest[28082:1424378] 0
xctest[28082:1424378] --> 1
xctest[28082:1424378] Simple string output
xctest[28082:1424378] 0
xctest[28082:1424378] --> 4
Of course we won't see the desired string "2 Placeholders. 0 Vars %# --- %#" as the crash happened before.
So, the question is now: Why is the amount of arguments now being 4 instead of 0? As none being passed in the second call, are the arguments being collected when the function is being called immediately again?
So, I started to call the function "again" to make sure the argument's list is being cleared, although va_end was being called:
__attribute__((overloadable)) void func1(NSString* _Nonnull format, ...)
{
va_list argList;
va_start(argList, format);
prefixAndArguments(none, nil, format, argList);
va_end(argList);
NSString *obj = nil;
prefixAndArguments(none, nil, obj, nil);
}
This does work now like a charm (argument's list is being cleared and the desired output is being received):
xctest[28411:1453508] 0
xctest[28411:1453508] --> 1
xctest[28411:1453508] Simple string output
xctest[28411:1453508] 0
xctest[28411:1453508] --> 1
Error occured when logging: amount of arguments does not for to the defined format. Callstack: ....
xctest[28411:1453508] 2 Placeholders. 0 Vars %# --- %#
Here is finally my question:
What is the reason for this behavior and how can I avoid it? Is there a better way to solve the issue than "stupidly" calling the function a second time with "no" arguments to clear the them?
P.s. I tried not to use macros, because I consider them as more error prone than c functions. See this thread: Macro vs Function in C
You appear to have some misconceptions about variadic functions, exemplified by this approach to counting the variable arguments:
while (va_arg(argList, NSObject *))
{
argCount += 1;
}
That code assumes that the variable arguments have at least one member, that all of them are of type NSObject *, and that the list will be terminated by a null pointer of that type. None of those is guaranteed by the system, and if those assumptions are not satisfied then the behavior of one or more va_arg() invocations will be undefined.
In practice, you can probably get away with actual arguments that are pointers of other types (though formally, the behavior will still be undefined in that case). If the arguments may have non-pointer types, however, then that approach to counting them is completely broken. More importantly, your test cases appear to assume that the system will provide a trailing NULL argument, but that is in no way guaranteed.
If your function relies on the end of the variable argument list being signaled by a NULL argument, then it is relying on the caller to provide one. It is very likely the absence of null termination in your argument lists that gives rise to the behavior you are asking about.
I'm dealing with the garmin GDL90 protocol which sends across various types of messages in binary to my IOS device. I'm going through and trying to process all these messages but have been running into an issue. Specifically the messages are byte packed so that if you ever see an occurrence of
0x7d 0x5e or 0x7d 0x5d you have to convert them to 0x7d or 0x7e
I've set my code up so that I detect the message type I'm parsing and then call a function:
- (void) parseMessage:(NSMutableData *)message
to do my data parsing. My individual message parsing functions call the parent function [super parseMessage:message]; which handles both the parsing of common elements as well as dealing with my byte-stuffing. Each of these function calls takes an NSData * so shouldn't a modification made in my super function return back out the same data?
My top level class gets a parse message call and the NSMutableData pointer's address is: 0x170048f10
Once I step into the parent's parseData call my address is still 0x170048f10
After I make modifications to the data I'm now pointing at the memory address 0x17805e840
Once I return from this function, however, I'm back pointing at 0x170048f10 again with the wrong data.
Should I be using pass by reference or something? Any suggestions?
I have two variations of my function - unstuff1 throws an error and unstuff2 doesn't work.
- (NSMutableData *)unstuff1:(NSMutableData *)mutableData {
int dataLength = [mutableData length];
char *bytes = [mutableData bytes];
// Scan bytes ignoring 1st and last byte because they will be 7e's
for (int i = dataLength - 1; i > 0; i--) {
bytes[i + 1] ^= 0x20;
if (i + 1 == dataLength) {
NSLog(#"Terminal character padding detected on character %d with length %d", i, dataLength);
} else {
/* Replace 2 bytes with a single byte should remove the flag when you do this */
[mutableData replaceBytesInRange:NSMakeRange(i, 2) withBytes:&bytes[i + 1] length:1];
dataLength--;
}
}
return mutableData;
}
- (NSMutableData *)unstuff2:(NSMutableData *)data {
NSMutableData *mutableData = [[NSMutableData alloc] initWithData:data];
int dataLength = [mutableData length];
char *bytes = [mutableData bytes];
// Scan bytes ignoring 1st and last byte because they will be 7e's
for (int i = dataLength - 1; i > 0; i--) {
bytes[i + 1] ^= 0x20;
if (i + 1 == dataLength) {
NSLog(#"Terminal character padding detected on character %d with length %d", i, dataLength);
} else {
/* Replace 2 bytes with a single byte should remove the flag when you do this */
[mutableData replaceBytesInRange:NSMakeRange(i, 2) withBytes:&bytes[i + 1] length:1];
dataLength--;
}
}
return mutableData;
}
In unstuff2 obviously i'm making a new MutableData so I guess that accounts for the memory address change (that is the function i was using that gave me the error specified).
unstuff1 throws the following exception:
-[_NSInlineData replaceBytesInRange:withBytes:length:]: unrecognized selector sent to instance 0x178250d40
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSInlineData replaceBytesInRange:withBytes:length:]: unrecognized selector sent to instance
Unlike languages like C++ and C# (just to name two), Objective C has no concept of "pass by reference". However, passing a copy of a pointer to your NSMutableData is functionally equivalent to passing the object by reference. That is, if you pass in an NSMutableData (or NSMutableAnything for that matter) to a function and that function modifies it, the calling function will see the changes reflected in the object that it passed in.
Ok looks like I tracked down the problem. I realized the compiler was sending out warnings:
incompatible pointer types initializing 'NSMutableData *' with an expression of type 'NSData *'
It turns out I had some code
NSMutableData *message = [data subdataWithRange:NSMakeRange(5, len - 5)];
Which i needed to convert into:
NSMutableData *message = [NSMutableData dataWithData:[data subdataWithRange:NSMakeRange(5, len - 5)]];
And then things all work out. Moral of the story (read your warnings!!!!)
I'm working through some exercises and have got a warning that states:
Implicit conversion loses integer precision: 'NSUInteger' (aka 'unsigned long') to 'int'
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSArray *myColors;
int i;
int count;
myColors = #[#"Red", #"Green", #"Blue", #"Yellow"];
count = myColors.count; // <<< issue warning here
for (i = 0; i < count; i++)
NSLog (#"Element %i = %#", i, [myColors objectAtIndex: i]);
}
return 0;
}
The count method of NSArray returns an NSUInteger, and on the 64-bit OS X platform
NSUInteger is defined as unsigned long, and
unsigned long is a 64-bit unsigned integer.
int is a 32-bit integer.
So int is a "smaller" datatype than NSUInteger, therefore the compiler warning.
See also NSUInteger in the "Foundation Data Types Reference":
When building 32-bit applications, NSUInteger is a 32-bit unsigned
integer. A 64-bit application treats NSUInteger as a 64-bit unsigned
integer.
To fix that compiler warning, you can either declare the local count variable as
NSUInteger count;
or (if you are sure that your array will never contain more than 2^31-1 elements!),
add an explicit cast:
int count = (int)[myColors count];
Contrary to Martin's answer, casting to int (or ignoring the warning) isn't always safe even if you know your array doesn't have more than 2^31-1 elements. Not when compiling for 64-bit.
For example:
NSArray *array = #[#"a", #"b", #"c"];
int i = (int) [array indexOfObject:#"d"];
// indexOfObject returned NSNotFound, which is NSIntegerMax, which is LONG_MAX in 64 bit.
// We cast this to int and got -1.
// But -1 != NSNotFound. Trouble ahead!
if (i == NSNotFound) {
// thought we'd get here, but we don't
NSLog(#"it's not here");
}
else {
// this is what actually happens
NSLog(#"it's here: %d", i);
// **** crash horribly ****
NSLog(#"the object is %#", array[i]);
}
Change key in Project > Build Setting
"typecheck calls to printf/scanf : NO"
Explanation : [How it works]
Check calls to printf and scanf, etc., to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense.
Hope it work
Other warning
objective c implicit conversion loses integer precision 'NSUInteger' (aka 'unsigned long') to 'int
Change key "implicit conversion to 32Bits Type > Debug > *64 architecture : No"
[caution: It may void other warning of 64 Bits architecture conversion].
Doing the expicit casting to the "int" solves the problem in my case. I had the same issue. So:
int count = (int)[myColors count];
Silly as it may sound, I am trying to write a simple function in objective-c which returns a string and displays it, the following code nearly works but I can't get printf to accept the functions return value ...
NSString* getXMLElementFromString();
int main(int argc, char *argv[])
{
printf(getXMLElementFromString());
return NSApplicationMain(argc, (const char **) argv);
}
NSString* getXMLElementFromString() {
NSString* returnValue;
returnValue = #"Hello!";
return returnValue;
}
NSString* is not equivalent to a traditional C string, which is what printf would expect. To use printf in such a way you'll need to leverage an NSString API to get a null-terminated string out of it:
printf("%s", [getXMLElementFromString() UTF8String]);
You should instead use NSLog() which takes a string (or a format string) as a parameter.
You could use either
NSLog(getXMLElementFromString());
or
NSLog(#"The string: %#", getXMLElementFromString());
Where the %# token specifies an Objective-C object (in this case an NSString). NSLog() works essentially the same as printf() when it comes to format strings, only it will also accept the object token.
I don't know that printf can handle an NSString. Try somethign like:
printf ("%s\n", [getXMLElementFromString()cString]);
I'm trying to read in and parse an xml document in an iPhone app. I begin parsing and then use the override method:
static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI,
int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes)
I then try to convert the attributes to a string with:
NSString *str1 = [[NSString alloc] initWithCString:attributes encoding:NSUTF8StringEncoding];
Why does the attributes parameter have two ** in front of it. And why when trying to extract the data and convert it to a string with the above code do I get the warning:
passing argument 1 of 'initWithCString:encoding:' from incompatible pointer type.
The documentation for libxml's start element callback states that the pointer is to an array that hold 5 values for each attribute (the number of attributes is returned in nb_attributes). This means that every 5th value in the array is a new attribute item.
The five items for each attribute are:
localname (the name of the attribute)
prefix (the namespace of the attribute)
URI
[start of] value (a pointer to the start
of the xmlChar string for the value)
end [of value] (a pointer to the end of the
xmlChar string for the value)
So you need to step through the array, get each value out of the items for the first attribute, then use the start value pointer to get the xmlChar string that is length = end - start. Then start over with the next attribute till you read in nb_attributes worth.
If that makes your head ache then I strongly suggest you switch to Apple's NSXMLParser (link may require login, or use this link NSXMLParser). In which case you would get the attributes as an NSDictionary. To get all the attributes out of it you could do the following:
for (NSString *attributeName in [attributeDict allKeys]) {
NSString *attributeValue = [attributeDict objectForKey:attributeName];
// do something here with attributeName and attributeValue
}
If you have access to the iPhone developer site then look at the example SeismicXML.
The sample is great except for two things:
you need to bump 'i' by 5 after each loop since there are 5 items for each attribute.
doing strlen() on both begin and end is expensive; it's easier to simply subtract begin from end
for (int i = 0; i < nb_attributes*5; i += 5)
{
const char *attr = (const char *)attributes[i];
const char *begin = (const char *)attributes[i + 3];
const char *end = (const char *)attributes[i + 4];
int vlen = end - begin;
char val[vlen + 1];
strncpy(val, begin, vlen);
val[vlen] = '\0';
NSLog(#"attribute %s = '%s'", attr, val);
}
The accepted answers explanation is correct, but it's helpful to view some example code too. Here is just one way to extract the value from the attributes, at least it works when I tested it. I'm far from being a C guru though.
for (int i = 0; i < nb_attributes; i += 5) {
const char *attr = (const char *)attributes[i];
const char *begin = (const char *)attributes[i + 3];
const char *end = (const char *)attributes[i + 4];
int vlen = strlen(begin) - strlen(end);
char val[vlen + 1];
strncpy(val, begin, vlen);
val[vlen] = '\0';
NSLog(#"attribute %s: %d = %s", attr, i, val);
}
NSXMLParser is nice, but from what I can tell, it downloads the entire XML before processing. Using libxml it can read in chunks at a time. It allows greater flexibility, but higher learning curve.
The '**' notation means "pointer to a pointer." In C/C++, a "string" is represented by an array of characters. An array is actually just a pointer under the covers, so a string in C/C++ can actually be declared as either "char[]" or "char*". The [] notation compiles down to a pointer to an array.
A common example of this is the typical "main" function in C/C++:
int main(int argc, char **argv)
Which is equivalent to:
int main(int argc, char *argv[])
argv is an array of char* "strings" (the command-line arguments to the program).
I can't provide an example at the moment, but it looks like you need to iterate over attributes to access the individual strings. For example, attributes[0] would be the first attribute string (an xmlChar*). You should be able to convert each individual attribute to an NSString.
const xmlChar **namespaces is an array of CStrings (int nb_namespaces tells you how many). If you want each namespace as an NSString, you could do something like the following:
NSMutableArray *namespaces = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < nb_namespaces; i++) {
NSString *namespace = [[NSString alloc] initWithCString:attributes[i] encoding:NSUTF8StringEncoding];
[namespaces addObject:namespace];
}
The initWithCString method is expecting xmlChar *, which is a pointer to an xmlChar (the first char in a CString).
xmlChar ** means pointer to a pointer to an xmlChar (the first char in the first CString).