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.
Related
I have a NSString in an NSArray and I wanted to order this string/fields based on how important it is. So say the string is B, H, A, Q, Z, L, M, O.
I wanted it to be always sorted as A, Q, Z, B, H, O, L, M. This is a predefined set of rule. How do I do this? Can this be done using NSSortDescriptor?
The short answer: Yes!
And here's how...
Since there are two pieces of information you need to know about your value (the importance and the value itself), you should create an object with these two important pieces of information, then store in an array similar to the way you store your strings. This makes it simple if, say, you want to change the 'importance' some time later with very little effort:
#interface MyObject : NSObject
#property (nonatomic, readwrite) NSInteger sortOrder;
#property (nonatomic, retain) NSString *value;
#end
#implementation MyObject
#synthesize sortOrder;
#synthesize value;
-(NSString *)description
{
//...so I can see the values in console should I NSLog() it
return [NSString stringWithFormat:#"sortOrder=%i, value=%#", self.sortOrder, self.value];
}
-(void)dealloc
{
self.value = nil;
[super dealloc];
}
#end
Add your objects to an array. Then sort:
NSMutableArray *myArrayOfObjects = [NSMutableArray array];
//Add your objects
MyObject *obj = [[[MyObject alloc] init] autorelease];
obj.sortOrder = 1;
obj.value = #"A";
[myArrayOfObjects addObject:obj];
obj = [[[MyObject alloc] init] autorelease];
obj.sortOrder = 2;
obj.value = #"Q";
[myArrayOfObjects addObject:obj];
//Sort the objects according to importance (sortOrder in this case)
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES] autorelease];
NSArray *sortedArray = [myArrayOfObjects sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSLog(sortedArray); //<--See for yourself that they are sorted
NSArray has several sort functions. Three you might consider are:
- (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr
- (NSArray *)sortedArrayUsingSelector:(SEL)comparator
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context
I think you might find the second, selector-based comparator the easiest to use to get started. See the docs here:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html#//apple_ref/occ/instm/NSArray/sortedArrayUsingSelector:
EDIT:
I think using NSSortDescriptor may be overkill, but here is a good post describing it:
How to sort NSMutableArray using sortedArrayUsingDescriptors?
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
How do I build up a global like integer array?
I tried variations of the following:
#interface
int *iArray; //this space will vary depending upon need in the implementation
#implementation
...
int iArrayInit[4] = {1,2,3,4};
iArray = iArrayInit;
-bottom line: I need to keep index values in array that I can access easily, and use of [NSArray intValue] maybe to slow.
thanks
If it needs to be static you can declare an NSMutableArray as static in the implementation file and expose static methods to access it. When using an NSArray the values need to be of type id which NSNumber can do. Here is an example which currently is not thread safe.
//.h file
#interface Foo : NSObject
{
}
+(NSArray*)iArray;
+(void)addiArrayValue:(NSNumber*)value;
#end
//.m file
#implementation Foo
static NSMutableArray *_iArray;
+(void)initialize
{
if([Foo class] == self)
{
_iArray = [[NSMutableArray alloc] init];
}
}
+(NSArray*)iArray
{
return [[_iArray copy] autorelease];
}
+(void)addiArrayValue:(NSNumber*)value
{
[_iArray addObject:value];
}
#end
//Use
[Foo addiArrayValue:[NSNumber numberWithInt:10]];
[Foo addiArrayValue:[NSNumber numberWithInt:12]];
NSLog(#"%#", [Foo iArray]);
I am newbie with Cocoa Touch, I have a problem that I try to figure out. I will appreciate if anyone could help.
I would like to create a tableDataList to display on the table. AsyncViewController will call TableHandler fillList method to initialize table data. But after fillList call, the tableDataList return empty.
Can anyone explain me this problem?
In "AsyncViewController.m":
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[self.myHandler fillList];
[super viewDidLoad];
}
In "TableHandler.m":
#implementation TableHandler
#define ItemNumber 20
#synthesize tableDataList;
- (void) fillList {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:(NSUInteger) 20];
for (NSUInteger i = 0; i < ItemNumber; i++) {
[array addObject:[NSString stringWithFormat:#"Row %d", i]];
}
tableDataList = [NSArray arrayWithArray:array];
}
Thanks
tableDataList needs to retain the new array, or it will be autoreleased soon after your call.
If tableDataList is a #property with retain, just change the line above to:
self.tableDataList = [NSArray arrayWithArray:array];
and the setter will handle it for you.
The equivalent of a #property (retain) NSArray *tableDataList; is in code,
- (void)setTableDataList:(NSArray *)anArray
{
if (tableDataList != nil) {
[tableDataList autorelease];
}
tableDataList = [anArray retain];
}
The above code will automatically release and retain objects when you replace the variable, using self.tableDataList = SOMETHING. However, if you just use tableDataList = SOMETHING you are not using the above setter, you're setting the variable directly.
Are you sure it's empty and not nil? You may need to alloc the tableDataList before you use it
I would like to fill in the class variables in a loop from an dictionary. What I want to do is having the dictionary key as a class variable and assign the class variable (the dictionary key) the value from dictionary... something like this:
+(void) initWithDictionary:(NSDictionary *)dic {
MyClass *classInstance = [[[self alloc] init] autorelease];
NSArray *allKeys = [dic allKeys];
for(NSUInteger i = 0; i < [allKeys count]; i++)
{
id classVariable = [allKeys objectAtIndex:i];
classInstance.classVariable = [dic objectForKey:[allKeys objectAtIndex:i]];
}
return classInstance;
}
It does not work, because I do not know how to assign the class variable from the string.
Thanks for answer, I am returning a JSON string that gives me an NSDictionary with keys and values. I am trying to fill this values to my class, let's say DetailObject. I want to use later in the project the DetailObject.id, DetailObject.description, etc. I would like to do it in a loop, becouse now I have to write this:
+ (id) initWithDiccionary :(NSDictionary *)dic//;
{
//Instantiating an object of this class... that's okay.
DetailObject *classInstance = [[[self alloc] init] autorelease];
classInstance.id = [dic objectForKey#"id"];
classInstance.desc = [dic objectForKey#"desc"];
etc... etc...
return classInstance;
}
What I want is to parse the dictionary from JSON to my object and respective variables and values that comes from dictionary in a loop, because if the JSON dictionary changes, I just add the new class variable with the same name of the returned dictionary key...
I do not know if I have explained it well...
Your question is very very unclear and I have no idea what you're trying to do or why. But just looking at your code I can tell you already that it's definitely not doing what you want.
//There should be no semicolon after "dic" below.
//Also, you should be returning a MyClass or an id.
- (id) initWithDiccionary :(NSDictionary *)dic//;
{
//Instantiating an object of this class... that's okay.
MyClass *classInstance = [[[self alloc] init] autorelease];
//Getting all the keys from the dictionary, seems fine...
NSArray *allKeys = [dic allKeys];
//Looping through all the keys in the dictionary, seems okay...
for(NSUInteger i = 0; i < [allKeys count]; i++)
{
//Storing the current key.
id classVariable = [allKeys objectAtIndex:i];
//Assigning the class's property "classVariable" to match the current key's value.
//No reason to say "[allKeys objectAtIndex:i]" again, though.
classInstance.classVariable = [dic objectForKey:classVariable];
}
//Returning something when you have no return type above (void) is wrong.
return classInstance;
}
Your code will just assign classInstance.classVariable to be equal to [allKeys objectAtIndex:[allKeys count]-1]. Your loop is pointless.
After I actually annotated your code though I think I have some idea of what you want. Basically you want to assign the variables with names matching the keys in the dictionary the values in the dictionary. i.e. if there is a key called "superKey" then you want to find the variable within classInstance (classInstance.superKey) and assign it the value in the dictionary that matches superKey. That's what you want, right?
Well, the only way I know of to do that is to use a big switch statement or a bunch of if statements. Make some function within MyClass like this:
- (void) assignProperty:(id)property toValue:(id)value
{
if (property == #"superKey")
{
self.superKey = value;
}
else if (property == #"lameKey")
{
self.lameKey = value;
}
//etc.
}
Then you just call [classInstance assignProperty:classVariable toValue:[doc objectForKey:classVariable]] and the job will be done.
But having told you all that...
Why would you ever want to do what you're doing? Want to know a much better way of doing this? Give MyClass its own NSDictionary. Basically all you are doing is defeating the entire purpose of the dictionary. Why? They are incredibly fast to access and can store whatever you want. There is no reason not to use one. So just do this:
- (id) initWithDiccionary :(NSDictionary *)dic
{
MyClass *classInstance = [[[self alloc] init] autorelease];
classInstance.dictionary = dic;
return classInstance;
}
Voila.
Enter Key-Value Coding. The following is an example of how you could achieve your desired outcome:
#interface MyClass : NSObject
#property (nonatomic, retain) NSString *aString;
#property (nonatomic, retain) NSNumber *aNumber;
#property (nonatomic, retain) NSString *yetAnother;
- (id)initWithDictionary:(NSDictionary *)dictionary;
#end
#implementation MyClass
#synthesize aString;
#synthesize aNumber;
#synthesize yetAnother;
- (id)initWithDictionary:(NSDictionary *)dictionary {
if ((self = [super init])) {
[self setValuesForKeysWithDictionary:dictionary];
}
return self;
}
// dealloc is left as an exercise for the reader
#end
You could use this class as follows:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"my string", #"aString",
[NSNumber numberWithInt:42], #"aNumber",
#"strings!", #"yetAnother", nil];
MyClass *myClass = [[MyClass alloc] initWithDictionary:dictionary] autorelease];
// yay!
You can thank Objective-C's dynamism for that. :)