Pass by value in Objective-C? - iphone

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];
}

Related

NSMutableArray Crashing App

In one class, I define an NSMutableArray with getters and setters:
#interface ArrayClass : NSObject {
NSMutableArray *array;
}
#property (nonatomic, strong) NSMutableArray *array;
#end
Then within the implementation file, I alloc init the mutable array:
#import "ImageUploader.h"
#implementation ArrayClass
#synthesize array;
- (id)init {
self = [super init];
if (self != nil) {
NSLog(#"ArrayClass inited");
array = [[NSMutableArray alloc] init];
}
return self;
}
#end
Then I initialize an instance of this class from another class:
ArrayClass *arrayClass = [[ArrayClass alloc] init];
[arrayClass.array addObject:image];
NSUInteger count = [arrayClass.array count];
NSLog(#"%#", count);
But when I try to add an object to the mutable array, the app crashes and Xcode 4.3 shows:
Removing the addObject call makes the app run fine. What am I doing wrong that would cause the app to crash?
This is wrong:
NSUInteger count = [arrayClass.array count];
NSLog(#"%#", count);
You want:
NSLog(#"%u", count);
%# is used to specify that the argument is an object. However, an NSUInteger is a primitive value, not an object. You use %u for unsigned ints.
try:
NSLog(#"%i", count);
NSUInteger return an INT not an object address
NSLog(#"%#", count);
is wrong, use:
NSLog(#"%i", count);
%# in the format statement expects and must be an object, NSUInteger is in int, not an object.
You are using an %# format specifier, which is for Cocoa objects only, for an NSUInteger, which is a typedef on an ordinary unsigned int. Use %d, %i or %u instead.
It looks to me like it's crashing when trying to print description, which makes sense because you're using %# where an integer is expected in your NSLog().
Separately, using a mutable property is almost always a bad idea. If it's really a property, you probably want to use an immutable array, and set the whole array when you want to change it.
Agree that the logging of count is wrong, but I think the other answers miss a move obvious point: the crash happens on the addObject. This implies that image is nil. NSLog that before the add.

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

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) {
// ...
}

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

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)

how to pass variable arguments to another method?

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.