Sorting 2-dimensional arrays - iphone

I have the following case. I import data from an xml feed and from facebook graph api, in this case posts. I want to merge this data in a array and sort this on the included date data.
I have now the following:
[containerArray addObject: [NSMutableArray arrayWithObjects: created_time, message, picture, fbSource, nil ]
];
This creates a 2-dimensional array, but i want to order all the entries on created_time.
How can i best solve this problem? Thnx in advance!!

Create a data class containing the necessary instance variables instead of the mutable array. Then you can use the various sort method of the NSArray class, for example sortedArrayUsingDescriptors.
A sort could look like this:
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"created_time"
ascending:YES] autorelease];
NSArray *sortedArray = [containerArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
EDIT
To quote Mr. Fowler from his book Refactoring: Improving the Design of Existing Code.
Replace Array with Object
You have an array in which certain elements mean different things.
Replace the array with an object that has a field for each element
...
Motivation
Arrays are a common structure for organizing data. However, they should be used only to contain a collection of similar objects in somre order.
That's what we want to do here. Let's create a simple Posts class. You can easily add your custom initializer which accepts the four values as parameters, or even a convenience class method to return an autoreleased object later on. This is just a basic skeleton:
Post.h
#interface Posts : NSObject
{
NSDate *created_time;
NSString *message;
UIImage *picture;
id fbSource; // Don't know what type :)
}
#property (nonatomic, retain) NSDate *created_time;
#property (nonatomic, copy) NSString *message;
#property (nonatomic, retain) UIImage *picture;
#property (nonatomic, retain) id fbSource;
#end
Post.m
#import "Post.h"
#implementation Post
#synthesize created_time, message, picture, fbSource;
#pragma mark -
#pragma mark memory management
- (void)dealloc
{
[created_time release];
[message release];
[picture release];
[fbSource release];
[super dealloc];
}
#pragma mark -
#pragma mark initialization
- (id)init
{
self = [super init];
if (self) {
// do your initialization here
}
return self;
}
EDIT 2
Adding a Post object to your array:
Post *newPost = [[Post alloc] init];
newPost.reated_time = [Date date];
newPost.message = #"a message";
newPost.picture = [UIImage imageNamed:#"mypic.jpg"];
// newPost.fbSource = ???
[containerArray addObject:newPost];
[newPost release];

Related

objective-c beginner: getter setter prob and EXC_BAD_ACCESS error

Iam getting an EXC_BAD_ACCESS all the time and I cannot figure out why...
Simple task:
The Parser Class pases XML with touchXML in an NSMutableArray called listArray.
In the Method grabCountry I can access the listArray and listArray.count works well.
Now I need the listArray.count in another Class the MasterViewController.
But Im getting an EXC_BAD_ACCESS error all the time.
Please help!
Here is the code snipplet:
Parser.h
#import <Foundation/Foundation.h>
#interface Parser : NSObject
#property (strong, retain) NSMutableArray *listArray;
#property (strong, retain) NSURL *url;
-(void) grabCountry:(NSString *)xmlPath;
#end
Parser.m
#import "Parser.h"
#import "TouchXML.h"
#implementation Parser
#synthesize listArray;
#synthesize url;
-(void) grabCountry:(NSString *)xmlPath {
// Initialize the List MutableArray that we declared in the header
listArray = [[NSMutableArray alloc] init];
// Convert the supplied URL string into a usable URL object
url = [NSURL URLWithString: xmlPath];
//XML stuff deleted
// Add the blogItem to the global blogEntries Array so that the view can access it.
[listArray addObject:[xmlItem copy]];
//works fine
NSLog(#"Amount: %i",listArray.count);
}
#end
MasterViewController.h
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TouchXML.h"
#import "Parser.h"
#class Parser;
#interface MasterViewController : UITableViewController{
Parser *theParser;
}
#end
MasterViewControlelr.m
- (void)viewDidLoad
{
NSString *xmlPath = #"http://url/to/xml.xml";
theParser = [[Parser alloc] init];
//Starts the parser
[theParser grabCountry:xmlPath];
//Here I want to access the Array count, but getting an BAD ACCESS error
NSLog(#"Amount %#",[theParser.listArray count]);
[super viewDidLoad];
}
Can anyone explain me what the problem here is?
Thanks!
Internally, each #property has a corresponding instance variable.
In your -grabCountry method, you are directly accessing the instance variable in the statement listArray = [[NSMutableArray alloc] init]; (same with url = [NSURL URLWithString: xmlPath];), instead of the #property's setter method, causing the NSMutableArray that you alloc-init'd to not be retained by the property. To invoke the #property's setter method, you should call
NSMutableArray *temp = [[NSMutableArray alloc] init];
self.listArray = temp; // or [self setListArray:temp];
[temp release];
If you want to have Xcode show an error when you are directly accessing the instance variable of an #property, you can have #synthesize listArray = _listArray, which changes the name of the instance variable to _listArray.
Generally, if there is an alloc-init, there must be a corresponding release (except if using Automatic Reference Counting).
Also, in the [listArray addObject:[xmlItem copy]]; statement, the call to copy is not needed, as NSArrays retain every object that is added to them. Calling copy also increases the retain count, which is another leak. Instead, you should just have [self.listArray addObject:xmlItem];
You are getting EXC_BAD_ACCESS because in NSLog(#"Amount %#",[theParser.listArray count]);, you are using %# format specifier, which is for NSStrings. You want to print the array's count, an integer, so you should be using %d or %i.

iPhone - Objective-C - Memory Leak with initWithArray

I am using the code below to set my two NSArray ivars:
The issue is, I keep getting a memory leak on the following lines:
followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
Is this not the correct way to set ivars from an existing NSArray of items? Any help would be appreciated. I've also tried to autorelease the above two lines, but when I actually access them in another method I get an error that they've already been released.
I have included my Interface and Implementation code below:
Interface .h:
NSArray *followingFriendsArray;
NSArray *followerFriendsArray;
#property (nonatomic, retain) NSArray *followingFriendsArray;
#property (nonatomic, retain) NSArray *followerFriendsArray;
Implementation .m:
- (void)handlerGetFollowingInformation:(id)value {
BOOL success = [Utility checkWebServiceErrors:value controller:self.navigationController];
if (success) {
Friend *friend = (Friend *)value;
followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
}
}
This is how I need to access the arrays:
- (void)followersButtonTapped:(id)sender {
FollowingVC *fvc = [[FollowingVC alloc] initWithNibName:#"FollowingViewController" bundle:nil];
fvc.friends = followerFriendsArray;
[self.navigationController pushViewController:fvc animated:YES];
[fvc release];
}
I release my two ivars in the following way as per usual:
- (void)viewDidUnload {
self.followingFriendsArray = nil;
self.followerFriendsArray = nil;
[super viewDidUnload];
}
- (void)dealloc {
[followingFriendsArray release];
[followerFriendsArray release];
[super dealloc];
}
I mean the code works just fine, it's just that I'm concerned about said memory leaks when I run the "Leaks" performance tool.
OK
you should not use autorelease in this case, but you have to release the arrays by calling :
[followingFriendsArray release];
[followerFriendsArray release];
you can do it:
when you don't need to use them any more.
in the dealloc method in your .m file.
option 2looks like that -
- (void)dealloc {
[followingFriendsArray release];
[followerFriendsArray release];
[super dealloc];
}
BTW -
if you don't manipulate the arrays after creating them (add / remove objects) you should use an immutable array (NSArray).
Good Luck
Your method handlerGetFollowingInformation is assigning new values to followingFriendsArray and followerFriendsArray without releasing the previous contents. If you call this method more than once on the same instance you will leak.
CRD is right that the arrays are not released inside the handlerGeFollowingInformation method but the fix is maybe overkill. What you need to do is to use self. so that the setter method is called which does that automatically. You could should look like this:
- (void)handlerGetFollowingInformation:(id)value {
BOOL success = [Utility checkWebServiceErrors:value controller:self.navigationController];
if (success) {
Friend *friend = (Friend *)value;
self.followingFriendsArray = [[NSArray alloc] initWithArray:friend.Following];
self.followerFriendsArray = [[NSArray alloc] initWithArray:friend.Followers];
}
}
Easy fix but hard to spot and I ran into this issue over and over again especially when I started to dealloc are the properties.
-Andy

iPhone: Memory Leak in Custom Class and NSMutableDictionary

I've spent a couple of days trying to find out what's going on. I have read loads of Memory Management documentation and I am sick to death of hearing "for every alloc you need a release" - I know that and I still can't figure out why my code is producing memory leaks.
I am writing a simple custom class with an NSMutableDictionary as one of its properties. Basically it mimics an XMLELement. I cannot for the life of me figure out why the allocation of a dictionary is causing a memory leak. The leak occurs on the device as well as the simulator - 5 leaks on the device, and 20 on the simulator.
The leak occurs when I declare and allocate the variable *tmp.
There is also a leak when I set the attribute details (name and value).
This is driving me nuts. Please help!
Part of the code:
#interface IMXMLElement : NSObject {
NSString *strElementName;
NSString *strElementValue;
NSMutableDictionary *dictAttributes;
}
#property (nonatomic, retain) NSString *strElementName;
#property (nonatomic, retain) NSString *strElementValue;
#property (nonatomic, retain) NSMutableDictionary *dictAttributes;
#end
#implementation IMXMLElement
#synthesize strElementName;
#synthesize strElementValue;
#synthesize dictAttributes;
-(id)initWithName:(NSString *)pstrName
{
self = [super init];
if (self != nil)
{
self.strElementName = pstrName;
**LEAK NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.dictAttributes = tmp;
[tmp release];
}
return self;
}
-(void)setAttributeWithName:(NSString *)pstrAttributeName
andValue:(NSString *)pstrAttributeValue
{
**LEAK [self.dictAttributes setObject:pstrAttributeValue forKey:pstrAttributeName];
}
-(void)dealloc
{
[strElementName release];
[strElementValue release];
[dictAttributes release];
[super dealloc];
}
The access this class using the following code:
NSString *strValue = [[NSString alloc] initWithFormat:#"Test Value"];
IMXMLElement *xmlElement = [[IMXMLElement alloc] initWithName:#"Test_Element"];
[xmlElement setAttributeWithName:#"id" andValue:strValue];
When you have strings as properties, declare them as copy, not retain.
NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.dictAttributes = tmp;
[tmp release];
the above is unnecessary, instead do:
(retain count will automatically be incremented for this autorelease object)
self.dictAttributes = [NSMutableDictionary dictionaryWithCapacity:0];
in dealloc do:
(retain count will automatically be decremented)
self.dictAttributes = nil;
normally for properties you just set them to nil instead of explicitly releasing them
since the get/setter handles that for you.
Try [dictAttributes removeAllObjects] before releasing dictAttributes.
Edit:
Also, you will positive allocation because you are allocating memory for "tmp". The memory will be retained because you now have a reference from dictAttributes.
You then have more positive allocation when you add elements to the dictionary, which also need to be allocated and are kept in memory by the dictionary's internal references
Typical syntax is NSMutableDictionary *tmp = [[[NSMutableDictionary alloc] init] autorelease];

How to convert an NSArray of NSManagedObjects to NSData

I'm new to Objective C and was wondering if anyone can help me.
I am using core data with a sqlite database to hold simple profile objects which have a name and a score attribute (both of which are of type NSString).
What I want to do is fetch the profiles and store them in an NSData object, please see my code below:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"GamerProfile" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sortDecriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDecriptors];
[sortDescriptor release];
[sortDecriptors release];
NSError *error;
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:items];
[self SendData:data];
[fetchRequest release];
When I run the code I'm getting the error "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[GamerProfile encodeWithCoder:]: unrecognized selector sent to instance 0x3f4b530'"
I presume I have to add an encodeWithCoderClass to my core data NSManagedObject object (GamerProfile) but I'm not sure how to do this even after reading the documentation, My attempt at doing this is below. I'm not sure if I'm going along the right lines with this as get a warning stating "NSManagedObject" may not respond to '-encodeWithCoder'"
I would really, really appreciate it if someone could point me in the right direction!!
Thanks
C
Here is the code for my GamerProfile (CoreData NSManagedObject Object) with my attempt at adding an encodeWithCoder method...
Header File
#import <CoreData/CoreData.h>
#interface GamerProfile : NSManagedObject <NSCoding>
{
}
#property (nonatomic, retain) NSString * GamerScore;
#property (nonatomic, retain) NSString * Name;
- (void)encodeWithCoder:(NSCoder *)coder;
#end
Code File
#import "GamerProfile.h"
#implementation GamerProfile
#dynamic GamerScore;
#dynamic Name;
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:GamerScore forKey:#"GamerScore"];
[coder encodeObject:Name forKey:#"Name"];
}
I got this to work. Here's how.
First create an NSValueTransformer like so:
ArrayToDataTransformer.h
#interface ArrayToDataTransformer : NSValueTransformer {
}
#end
ArrayToDataTransformer.m
import "ArrayToDataTransformer.h"
#implementation ArrayToDataTransformer
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
//Take an NSArray archive to NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:value];
return data;
}
- (id)reverseTransformedValue:(id)value {
//Take NSData unarchive to NSArray
NSArray *array = (NSArray*)[NSKeyedUnarchiver unarchiveObjectWithData:value];
return array;
}
#end
The above is your interface to NSManagedObject, now create one that use it, for example:
Array.h
#import <CoreData/CoreData.h>
#class Arrays;
#interface Array : NSManagedObject
{
}
#property (nonatomic, retain) id myArray;
#property (nonatomic, retain) Arrays * arrayOfArrays;
#end
Array.m
#import "Array.h"
#import "Arrays.h"
#implementation Array
#dynamic myArray;
#dynamic arrayOfArrays;
#end
In the xcdatamodel, Array needs myArray Attributes set as Optional (usually always checked), and Type is: Transformable, and Value Transformer Name: ArrayToDataTransformer
Now you can use it;
NSMutableArray* positionArray;
positionArray = [[NSMutableArray alloc] arrayWithCapacity:[myArray count]];
for(NSArray *pos in myArray) {
[positionArray addObject:[NSString stringWithFormat:#"%#",pos]];
}
NSLog(#"ArrayCtrl : positionArray cnt = %d",[positionArray count]);
//Now add the positionArray to CoreData using the setValue & myArray Key
Array *array = (Array*)[NSEntityDescription insertNewObjectForEntityForName:#"Array" inManagedObjectContext:self.managedObjectContext];
[array setValue:positionArray forKey:#"myArray"];
[myArrays setMyArrays:array];
[self saveAction:array];
[positionArray release];
To retrieve the data from CoreData:
using a one-to-one relationship, thus myArrays points to just one array element
NSArray *positionArray = [myArrays.array valueForKey:#"myArray"];
If you are using a one-to-many, and things are named as above, you'll get back an NSSet.
Core Data should store the Array as a Blob in the database, and a large Array can be written very quickly, say one with 3,500 objects takes less than a second. The performance is comparable to how UIImage is stored and retrieved using pretty much the same concepts. The retrieval I think is even faster.
The alternative is to write each value of the Array individually into Core Data. For this you need to create the appropriate NSManageObject, but beware that you'll have to save 3,500 times for each array value, and for 3,500 items, this will take 20 to 30 seconds.
Thus the above method is great for writing large arrays into CoreData in one shot, and retrieving them also in one shot.
Spent a few hours on this one, was about to give up, and then I saw the light!
NSManagedObject and NSCoding really do not play well together. Consider this answer to a similar question for background and a possible solution.

Need to store array of an array of class objects in View Controller

An iPhone question for you guys! I have an NSURLConnection that downloads XML from a server and processes it into an array which is a part of another array. I have no way of knowing how many objects I will need, so I cannot allocate an NSArray beforehand. My question is:
Would it be better to create the parent array as an NSArray at the class level and allocate it after I store the data in a temporary NSMutableArray or just make the NSMutableArray at class level? It is worth noting that I do not need to modify the array other than to release it at the end of the program run.
I don't think it really matters.
I'm reading the Beginning iPhone 3 Development book at the moment, and usually loading the data is done like this:
You'd have an NSArray property :
#interface
{
...
NSArray *listOfObjects;
...
}
#property (nonatomic, retain) NSArray *listObObjects;
...
Then you create an NSMutableArray, load your data and set the property:
NSMutableArray *array = [[NSMutableArray alloc] init]; // ?
// load the XML into array here
...
self.listOfObjects = array;
[array release];
listOfObjects would then be treated as an NSArray (immutable) although it actually would be an NSMutableArray.
I think what you probably want to do is create some Classes that match what you are representing in your xml. For example if you xml looks something like this:
<peopleList>
<person>
<name>Joe</name>
<possession>Shovel</possession>
</person>
<person>
<name>Sam</name>
<possession>Shovel</possession>
<possession>Fork</possession>
<possession>Backpack</possession>
</person>
</peopleList>
You should have a PeopleList Class and a Person Class. An object instantiated from the PeopleList Class has your first array that contains one or more Person objects. The Person objects, in turn, also have arrays to hold the possesions (which in this case are strings - although if needed they could be Possesion objects) In this case, to help the example the Person Class also has another property: 'name' which is also a String.
For example:
#interface PeopleList {
NSMutableArray *persons; // An array to store the Person objects
}
#property (nonatomic, retain) NSMutableArray *persons;
#end
#interface Person {
NSString *name;
NSMutableArray *possesions; //An array to store this Person's possesion strings
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableArray *possesions;
#end
In the init method's of these objects you will have to alloc/init the arrays so they will be ready to receive objects. And because I alloc'd them, my class is responsible for the release.
#implementation PeopleList
-(id) init {
if (self = [super init]) {
persons = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[persons release];
[super dealloc];
}
#end
#implementation PeopleList
-(id) init {
if (self = [super init]) {
possesions = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[possesions release];
[super dealloc];
}
#end
Now that this is done, you can set up your data structure of cascading array's.
As you are parsing the XML when you come across a PeopleList Tag do a:
currentPeopleList = [[PeopleList alloc] init];
and when you come across a Person tage do a:
currentPerson = [[Person alloc] init];
[peopleList.persons addObject: person];
a possesion:
[currentPerson.possesion addObject: contentsOfCurrentElement];
or name:
currentPerson.name = contentsOfCurrentElement;
But to answer your more specific question, I not store the data in a temporary NSArray and then copy that into a NSMutableArray. There is almost no performance gain by doing so, and you will burn cpu cycles and memory doing the copy.