objective c: passing a char argument to function crashes the app - iphone

I wrote a singleton called SomeValues where I initialize a foo NSMutableArray. I then tried to write a function SetBFSV to set the values of this array from different control views.
#interface SomeValues : NSObject {
NSMutableArray *foo;}
+ (SomeValues *) sharedInstance;
#implementation
...
- (void) SetBFSV:(char)lbl ToVal:(long)BFSvl{
NSNumber *ValueBFSvl = [NSNumber numberWithLong:BFSvl];
NSString *Strlbl = [[NSString alloc] stringWithFormat:#"%s",lbl];
[foo setValue:ValueBFSvl forKey:Strlbl];
}
I know that setValue requires a NS object for both the value and the key, but I cannot declare my function as
(void) SetBFSV:(NSString)lbl ToVal:(NSNumber)BFSvl
because it doesn't compile with the error: "Can not use an object as parameter to a method".
In one ControlView I wrote then this piece of code:
SomeValues *myBFSV = [SomeValues sharedInstance];
const unsigned char *Bar = (unsigned char *)[#"Label1" UTF8String];
NSLog(#"The string %s", Bar);
[myBFSV SetBFSV:Bar ToVal:2.5];
When compiling I get a warning on the last line:
warning: passing argument 1 of 'SetBFSV:ToVal:' makes integer from pointer without a cast
Which integer? I'm getting stupid looking around for it. When running I get the print out from the NSLog, but right afterwards the program obviously crashes with this error:
'NSInvalidArgumentException', reason: '-[NSPlaceholderString stringWithFormat:]: unrecognized selector sent to instance 0x4e03280'
Clearly I'm passing something wrong to stringWithFormat but I cannot understand what.
Thanks for any help. Have a nice day!
/luca

Possible problems with your code (unless you have typos in it):
- (void) SetBFSV:(char)lbl ToVal:(long)BFSvl function expects char as its 1st parameter but you pass it a char* - your 1st warning probably comes from here
stringWithFormat is a class method so your code should look either:
NSString *Strlbl = [[NSString alloc] initWithFormat:#"%s",lbl];
[foo setValue:ValueBFSvl forKey:Strlbl]
or
NSString *Strlbl = [NSString stringWithFormat:#"%s",lbl];
[foo setValue:ValueBFSvl forKey:Strlbl]
since you try to use -stringWithFormat instead of +stringWithFormat you get a crash
If you pass char to a function then correct format specifier for it will be %c, not %s
You probably must release your Strlbl variable if you create it with alloc/init otherwise it will leak (but you must not release it if you use +stringWithFormat:)

For one, you can't use an object as a paramter:
(void) SetBFSV:(NSString)lbl ToVal:(NSNumber)BFSvl
You need to pass them as pointers, using the asterisk.
(void) SetBFSV:(NSString*)lbl ToVal:(NSNumber*)BFSvl
Furthermore, if you need to pass them as (char) and (long) your bit of code:
SomeValues *myBFSV = [SomeValues sharedInstance];
const unsigned char *Bar = (unsigned char *)[#"Label1" UTF8String];
NSLog(#"The string %s", Bar);
[myBFSV SetBFSV:Bar ToVal:2.5]; // in either case, this is a long. why are you passing a double here?
Passes *Bar as a pointer. You should really read up on the pointers and objects. IF you need to pass them in as (const char*) and (long) do it like this:
- (void) SetBFSV:(const char*)lbl ToVal:(long)BFSvl{
NSNumber *ValueBFSvl = [NSNumber numberWithLong:BFSvl];
NSString *Strlbl = [NSString stringWithUTF8String: lbl]; // your old code had a memory leak here. you need to either create an autorelease object, or release it after adding to `foo`
[foo setValue:ValueBFSvl forKey:Strlbl];
}
My Recommendation is to do the following:
- (void) SetBFSV:(NSString*)key ToVal:(long)val{
NSNumber *value = [NSNumber numberWithLong:val];
[foo setValue:value forKey:key];
}
And call it like the following (from your example):
SomeValues *myBFSV = [SomeValues sharedInstance];
NSString *Bar = [NSString stringWithString:#"Label1"];// this may not be necessary...
NSLog(#"The string %#", Bar);
[myBFSV SetBFSV:Bar ToVal:25]; // don't pass a decimal for a (long)

Related

Why is my Objective-C class instance variable not being set?

this is a noob question but I cannot for the life of me figure out why my MSMutableArray class variable is not being set. Below is my code:
#interface MyClass : NSObject {
NSMutableArray *imageArr;
}
#implementation MyClass
-(id)init
{
self = [super init];
if (self) {
imageArr = [[NSMutableArray alloc] init];
}
return self;
}
- (void) checkImageArr {
NSLog(#"imageArr size::%#",[imageArr count]);
}
When this code runs, the following is set to the log:
imageArr size::(null)
Why is that variable not being set? I looked at the following question and don't see anything different between my code and the accepted answer.
Thanks.
The %# specifier expects the argument to be an id (or pointer to an object). -count returns an NSUInteger which is not an object. Since your array is empty, the count is zero and so the argument is being interpreted as a nil object, which comes out as (null) when used with the %# specifier.
If the argument was not nil, -description would be sent to it to get a string to insert in the log message. So, if you add an object to your array, NSLog will try to interpret 1 as an object pointer and send -description to it. This will cause an EXC_BAD_ACCESS exception (try it!).
You need a format specifier that interprets the argument as a number. NSUinteger has the following definition
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef unsigned long NSUInteger;
#else
typedef unsigned int NSUInteger;
#endif
so it's probably an unsigned long. For safety, I always cast it to make sure, so you need this:
NSLog(#"imageArr size::%lu", (unsigned long)[imageArr count]);
Change %# to %lu and you'll see.
The count method returns an NSUInteger.
Try this log format instead:
NSLog(#"imageArr size::%u",[imageArr count]);
NSLog(#"imageArr size::%#",[imageArr count]);
You should use %# when you want to "write an object" AFAIK, it is like write [object description].
[imageArr count] is a int, right? It is not a pointer to an object.
You should use %i instead of %#. And size will be 0.
Try NSLog(#"imageArr size::%#",imageArr); if you want to write a address

Xcode Gives Strange Output From NSArray

When I run this code, the output is some 1084848 to the console. I can't figure out why such odd output... here is the code.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
int someNumber = 3;
[array addObject:[NSNumber numberWithInt:someNumber]];
NSLog(#"%i" , [array objectAtIndex:0]);
[pool drain];
return 0;
}
the "%i" format specifier expects an integer, not an Object.
Try NSLog(#"%i" , [[array objectAtIndex:0] intValue]);
XCode is probably giving you a warning on this line: something like "Conversion specifies type 'int', but argument has type 'id'".
Here's the pseudocode of your program:
//
// Inside of your main function....
//
// Set up the Autorelease pool and then create an array
//
// Declare an int
//
// Add the int to an array, while wrapping it in an NSNumber
//
// Log the value of the first object in the array, using the int formatter
//
// Clean up and return
//
You are logging the first object in the array, but NSArray cannot hold a primitive that's not wrapped in an Objective-C object.
To better understand your code, try changing these lines of code:
int someNumber = 3;
[array addObject:[NSNumber numberWithInt:someNumber]];
Expand them a little. Try this:
int someNumber = 3;
NSNumber *aNumber = [NSNumber numberWithInt:someNumber];
[array addObject:aNumber];
So, you've correctly wrapped the int in an NSNumber, but you're not unwrapping it. You need to ask your NSNumber for the int that it holds like so:
[[array objectAtIndex:0] intValue];
Or, to do the logging in one line:
NSLog(#"%i" , [[array objectAtIndex:0] intValue]);
The characters "%i" is called a "formatter". Different kinds of values require different formatters. When you are using an Objective-C object, you use "%#". For an NSInteger or int, you'd use %i. For a float, you'd use "%f". The point is that you need to either unwrap that number, or use the Objective-C formatter for strings.
A quick note about that weird value you were getting earlier: That's a memory address in RAM. It's the closest thing you're going to get when you use an incorrect formatter. In some cases, using the wrong formatter will cause an EXC_BAD_ACCESS. You were "lucky" and got a weird value instead of a dead program. I suggest learning about strings and formatters before you move on. It will make your life a lot easier.
When you use %i for integer values then you should give arguments as integer as below
NSLog(#"%i" , [[array objectAtIndex:0] intValue]);
But when you want object to be displayed then you must use %# which identifies object in general case as below:
NSLog(#"%#", array);

iOS -- get pointer from NSString containing address

If I have a pointer to an object foo with address (say) 0x809b5c0, I can turn that into an NSString by calling
NSString* fooString = [NSString stringWithFormat: #"%p", foo].
This will give me the NSString #"0x809b5c0".
What is the easiest way to reverse the process? That is, start with fooString, and get back my pointer to foo.
In its completeness...
To address-string
NSString *foo = #"foo";
NSString *address = [NSString stringWithFormat:#"%p", foo];
And back
NSString *fooAgain;
sscanf([address cStringUsingEncoding:NSUTF8StringEncoding], "%p", &fooAgain);
This snippet worked for me:
NSString *pointerString = #"0x809b5c0";
NSObject *object;
sscanf([pointerString cStringUsingEncoding:NSUTF8StringEncoding], "%p", &object);
Convert it to an integer, then cast it to whatever type it's supposed to be...
NSUInteger myInt = [ myStr integerValue ];
char * ptr = ( char * )myInt;
That said, I really don't understand why you will need this...
In my experience its easier to convert the pointer's address to an NSInteger, like this:
MyObject *object=[[MyObject alloc]init];
//...
NSInteger adress=(NSInteger)object;
Now reverse:
MyObject *otherObject=(MyObject*)adress;
Now: object==otherObject
Use an NSMapTable instead:
MyClass* p = [[MyClass alloc]init];
NSMapTable* map = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory];
[map setObject:whateverValue forKey:p];
// ...
for(MyClass* key in map) {
// ...
}

Pass by value in Objective-C?

I'm looking at understanding objective-c and I came into a problem in tapping the screen and incrementing the count variable which I store in my appdelegate.
- (void)updateLabel:(NSInteger)num {
NSString *s = [[NSString alloc] initWithFormat:#"%#", num];
countLabel.text = s;
[s release];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
TestAppDelegate *aDel = (TestAppDelegate *)[UIApplication sharedApplication].delegate;
aDel.count++;
NSInteger num = aDel.count;
[self updateLabel:num];
}
I get the EXC_BAD_ACS which to me says I'm trying to access something I'm not. It looks like I cannot send updateLabel the num variable because the scope of the primitive type goes away at the end of the method and then when updateLabel tries to access it, I get the error? I wanted to know if I understood this concept correctly. Thanks.
// format specifier for integer is %d, not %#
NSString *s = [[NSString alloc] initWithFormat:#"%d", num];
num is not out of scope here. You are passing it by value to updateLabel. Please also check that countLabel is not already released when you are calling updateLabel.
And you can pass aDel.count directly to the updateLabel. There is no need of temporary num variable.
[self updateLabel:aDel.count];
The problem might be that NSInteger is not an object, see its definition by cmd-clicking the keyword:
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE …
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
Which means that your method to update the label should look a bit like this:
- (void) updateLabel: (NSInteger) num {
countLabel.text = [NSString stringWithFormat:#"%i", num];
}

Would NSMutableArray -removeObject: also remove a NSString if it has a different memory adress?

Example: I add some NSString objects to an NSMutableArray: #"Foo", #"Bar", #“FooBar". Now somewhere else I access that array again, and I want to delete #"Foo". So I create a new NSString #"Foo", and pass it to -removeObject:. The documentation does not state on what criteria -removeObject works. I think that it looks only for the memory address, So in this case it would do nothing. Is that correct?
according to the doc for removeObject: "matches are determined on the basis of an object’s response to the isEqual: message" and strings are compared with hashes ("If two objects are equal, they must have the same hash value") so, YES, that should be incorrect.
Your example is an unfortunate one - if you use the string literal #"Foo" in two places in the code, the compiler will give them the same address (i.e. it uses the same static instance of the string). Example:
heimdall:Documents leeg$ cat foostrings.m
#import <Foundation/Foundation.h>
int main(int argc, char **argv)
{
NSString *string1 = #"Foo";
NSString *string2 = #"Foo";
printf("string1: %p\nstring2: %p\n", string1, string2);
return 0;
}
heimdall:Documents leeg$ ./foostrings
string1: 0x100001048
string2: 0x100001048