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
Related
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.
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);
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];
}
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)
I was wondering what were the default values of variables before I initialized them...
For example, if I do :
//myClass.h
BOOL myBOOL; // default value ?
NSArray *myArray; // default value ?
NSUInteger myInteger; // default value ?
Some more examples here :
//myClass.m
// myArray is not initialized, only declared in .h file
if ([myArray count] == 0) { // TRUE or FALSE ?
// do whatever
}
More generally, what is returned when I do :
[myObjectOnlyDeclaredAndNotInitialized myCustomFunction];
Thank you for your answers.
Gotye.
The answer is that it depends on the scope in which the variable is defined.
Instance variables of Objective-C objects are always initialised to 0/nil/false because the memory allocated is zeroed.
Global variables are probably initialised to 0/nil/false to because when memory is first allocated to a process, it is also zeroed by the operating system. However, as a matter of course, I never rely on that and always initialise them myself.
Local variables are uninitialised and will contain random data depending on how the stack has grown/shrunk.
NB for pointers to Objective-C objects, you can safely send messages to nil. So, for instance:
NSArray* foo = nil;
NSLog(#"%# count = %d", foo, [foo count]);
is perfectly legal and will run without crashing with output something like:
2010-04-14 11:54:15.226 foo[17980:a0f] (null) count = 0
by default NSArray value will be nil , if you would not initialize it.
//myClass.h
BOOL myBOOL; // default value ?
NSArray *myArray; // default value ?
NSUInteger myInteger; // default value ?
the Bool myBool if not initialized will receive the default value of False.
NSArray *myArray if not allocated,initialized(for example NSArray *myArray = [[NSArray alloc] init];) will have the default value of NIL. if you call count on an unitialized array you will receive an exception not 0.
//myClass.m
// myArray is not initialized, only declared in .h file
if ([myArray count] == 0) { => here it should crash because of [myArray count]
// do whatever
}
for the NSUInteger myInteger the default value should be 0, the documentation says that this is used to describe an unsigned integer(i didn;t represent integers with this one though)
when you call [myObjectOnlyDeclaredAndNotInitialized myCustomFunction]; it should break, throw an exception.
hope it helps