Xcode: Remove objects from NSMutableArray based on NSDictionary - xcode

I am new to tableViews and dictionaries and i have a problem!
In ViewDidLoad i am initializing many MutableArrays and i am adding data using NSDictionary. Example:
- (void)viewDidLoad {
nomosXiou=[[NSMutableArray alloc] init];
[nomosXiou addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"Mary",#"name",#"USA",#"country", nil]];
[nomosXiou addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"Peter",#"name",#"Germany",#"country", nil]];
[super viewDidLoad];
// Do any additional setup after loading the view.}
In a previous ViewController the user selects a Country. Based on that selection, how could i remove from my arrays all the other entries???
Thanks in advance...

First note that your code fragment has an error. It should read:
NSMutableArray *nomosXiou= [[NSMutableArray alloc] init];
There are a number of ways to do what you want, but the most straightforward is probably the following:
NSString *countryName; // You picked this in another view controller
NSMutableArray *newNomosXiou= [[NSMutableArray alloc] init];
for (NSDictionary *entry in nomosXiou) {
if ([[entry objectForKey:#"country"] isEqualToString:countryName])
[newNomosXiou addObject:entry];
}
When this is done newNomosXiou will contain only the entries in nomosXiou that are from the country set in countryName.

Something like this will do the job:
NSMutableArray *nomosXiou = [[NSMutableArray alloc] init];
NSString *country = #"Germany"; // This is what you got from previous controller
// Some test data. Here we will eventually keep only countries == Germany
[nomosXiou addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"Mary",#"name",#"USA",#"country", nil]];
[nomosXiou addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"Peter",#"name",#"Germany",#"country", nil]];
[nomosXiou addObject:[[NSDictionary alloc] initWithObjectsAndKeys:#"George",#"name",#"Germany",#"country", nil]];
// Here we'll keep track of all the objects passing our test
// i.e. they are not equal to our 'country' string
NSIndexSet *indexset = [nomosXiou indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop){
return (BOOL)![[obj valueForKey:#"country"] isEqualToString:country];
}];
// Finally we remove the objects from our array
[nomosXiou removeObjectsAtIndexes:indexset];

Related

Crash Occurring on First Launch When populating core data database

I keep getting an error in the debugger for my application saying,
2013-06-23 16:07:15.826 collection view recipies[5681:c07] -[NSManagedObject length]: unrecognized selector sent to instance 0x9495280
2013-06-23 16:07:15.827 collection view recipies[5681:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject length]: unrecognized selector sent to instance 0x9495280'
* First throw call stack:
(0x26ac012 0x1517e7e 0x27374bd 0x269bbbc 0x269b94e 0x2b11c4 0x16d80a 0x4464 0x64f2da 0x6508f4 0x652b91 0x19c2dd 0x152b6b0 0x18eefc0 0x18e333c 0x18eeeaf 0x23b2bd 0x183b56 0x18266f 0x182589 0x1817e4 0x18161e 0x1823d9 0x1852d2 0x22f99c 0x17c574 0x17c76f 0x17c905 0x185917 0x14996c 0x14a94b 0x15bcb5 0x15cbeb 0x14e698 0x2c06df9 0x2c06ad0 0x2621bf5 0x2621962 0x2652bb6 0x2651f44 0x2651e1b 0x14a17a 0x14bffc 0x1e9d 0x1dc5)
libc++abi.dylib: terminate called throwing an exception
In My application delegate, if check to see if the application is being launched for the first time, and if it is, I then add several image paths to the core data structure.
In AppDelegate.m under ApplicationDidFinishLaunchingWithOptions,
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// app already launched
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first launch ever
NSArray *mainDishImages = [NSArray arrayWithObjects:#"egg_benedict.jpg", #"full_breakfast.jpg", #"ham_and_cheese_panini.jpg", #"ham_and_egg_sandwich.jpg", #"hamburger.jpg", #"instant_noodle_with_egg.jpg", #"japanese_noodle_with_pork.jpg", #"mushroom_risotto.jpg", #"noodle_with_bbq_pork.jpg", #"thai_shrimp_cake.jpg", #"vegetable_curry.jpg", nil];
NSArray *drinkDessertImages = [NSArray arrayWithObjects:#"angry_birds_cake.jpg", #"creme_brelee.jpg", #"green_tea.jpg", #"starbucks_coffee.jpg", #"white_chocolate_donut.jpg", nil];
for (NSString *imagePath in mainDishImages) {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:#"Recipe" inManagedObjectContext:context];
[newRecipe setValue:imagePath forKey:#"imageFilePath"];
}
for (NSString *imagePath in drinkDessertImages) {
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:#"Deserts" inManagedObjectContext:context];
[newRecipe setValue:imagePath forKey:#"imageFilePath"];
}
}
And I access that data in my collectionViewController, I access that data.
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Deserts"];
deserts = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSFetchRequest *fetchRequestTwo = [[NSFetchRequest alloc] initWithEntityName:#"Recipe"];
meals = [[managedObjectContext executeFetchRequest:fetchRequestTwo error:nil] mutableCopy];
recipeImages = [NSArray arrayWithObjects:meals, deserts, nil];
[self.collectionView reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Deserts"];
deserts = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSFetchRequest *fetchRequestTwo = [[NSFetchRequest alloc] initWithEntityName:#"Recipe"];
meals = [[managedObjectContext executeFetchRequest:fetchRequestTwo error:nil] mutableCopy];
recipeImages = [NSArray arrayWithObjects:meals, deserts, nil];
UICollectionViewFlowLayout *collectionViewLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
collectionViewLayout.sectionInset = UIEdgeInsetsMake(5, 0, 5, 0);
self.navigationController.navigationBar.translucent = YES;
self.collectionView.contentInset = (UIEdgeInsetsMake(44, 0, 0, 0));
selectedRecipes = [NSMutableArray array];
}
According to crashalytics, the error is in the line where it says
recipeImageView.image = [UIImage imageNamed:[recipeImages[indexPath.section] objectAtIndex:indexPath.row]];
In the method
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[recipeImages[indexPath.section] objectAtIndex:indexPath.row]];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"photo-frame-selected.png"]];
return cell;
}
I hope you can help. Thanks In Advance!
The UIImage method imageNamed takes an NSString as argument, but you pass a NSManagedObject to it.
You should get the image path from the managed object first. Try this:
id managedObject = [recipeImages[indexPath.section] objectAtIndex:indexPath.row];
NSString* imagePath = [managedObject valueForKey:#"imageFilePath"];
recipeImageView.image = [UIImage imageNamed:imagePath];

How to Create UITableView with History of Button Clicks

I'm creating an app that should list the date and time when a button is clicked and put the list inside a UITableView. My idea is to get the date and timestamp every time the user taps the button and then save it in an array of dictionary objects of every time and date when the button was clicked. I'll also have another button that simply loads a modal view that displays the said UITableView with the list of the history of button clicks.
I was able to do it partially with my table getting populated with the number of dictionary entries inside the array. Problem is, it always end up with the same time and date for all of the entries.
Here's a screenshot of the table initially with one entry.
and this what happens when I tapped the button many times. It displays the updated time for all rows.
How can I display the history in the table and prevent it from being updated? I'm simply using NSUserDefaults also in saving the data.
Here's some code in my Button Clicked method:
- (IBAction)btnClicked:(id)sender
{
NSLog(#"Button pressed");
// Gets the current time and formats it
NSDate *timeNow = [NSDate date];
NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];
[timeFormatter setDateFormat:#"HH:mm a"];
// Gets the current date and formats it
NSDate *dateNow = [[NSDate alloc] init];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM dd, yyyy"];
NSString *currentTime = [timeFormatter stringFromDate:timeNow];
NSString *currentDate = [dateFormatter stringFromDate:dateNow];
NSString *timestamp = currentTime;
NSString *date = currentDate;
// This is where values gets saved
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:timestamp forKey:#"TimeStamp"];
[defaults setObject:date forKey:#"Date"];
[defaults synchronize];
NSString *time = [defaults objectForKey:#"TimeStamp"];
NSString *dateToday = [defaults objectForKey:#"Date"];
tableVC.tableDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:time, #"Time", dateToday, #"Date", nil];
[tableVC.tableArray addObject:tableVC.tableDict];
[tableVC.table reloadData];
}
This is my viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *time = [defaults objectForKey:kTimeStampText];
NSString *date = [defaults objectForKey:kDateText];
tableDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:time, #"Time", date, #"Date", nil];
tableArray = [[NSMutableArray alloc] initWithObjects:tableDict, nil];
}
This is my table methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *customCell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
if (customCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell"
owner:self
options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[CustomCell class]])
customCell = (CustomCell *)oneObject;
}
customCell.dateLbl.text = [tableDict objectForKey:#"Date"];
customCell.timeLbl.text = [tableDict objectForKey:#"Time"];
return customCell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 74;
}
Ok I think I found your problem, you are accessing the same date for each table row.
customCell.dateLbl.text = [tableDict objectForKey:#"Date"];
customCell.timeLbl.text = [tableDict objectForKey:#"Time"];
Is incorrect
NSDictionarry *dict = [tableArray objectAtIndex:[indexPath row]];
customCell.dateLbl.text = [dict objectForKey:#"Date"];
customCell.timeLbl.text = [dict objectForKey:#"Time"];
Now that should loop through it nicely and print all dates.
The problem with the code you had was that the row for index path method gets called for each row in the tableview, ie it is like a loop, so you assign each cells properties on it's own, hope this makes sense.

NSMutableArray Not Adding Objects

I have a main view controller that consists of 1 button that when tapped, pushes a detail view controller and another button that simply launches a modal view controller that has a table. This detail view controller has a button that when tapped should add a new object to the array that was loaded by the table in the modal view controller.
Problem is, that add a new object button doesn't add to the NSMutableArray.
When I put a button in the main view and assigned the same method of adding objects to the NSMutableArray, it works. It just doesn't work when the button is placed on another view controller presented either via Navigation Controller and Modal View.
Here's my code when the button is pressed in the detail view controller:
- (IBAction)confirmBtnPressed
{
// Gets the current time and formats it
NSDate *timeNow = [NSDate date];
NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];
[timeFormatter setDateFormat:#"hh:mm a"];
// Gets the current date and formats it
NSDate *dateNow = [[NSDate alloc] init];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM dd, yyyy"];
NSString *currentTime = [timeFormatter stringFromDate:timeNow];
NSString *currentDate = [dateFormatter stringFromDate:dateNow];
NSString *timestamp = currentTime;
NSString *date = currentDate;
// This is where values gets saved
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:timestamp forKey:kTimeStampText];
[defaults setObject:date forKey:kDateText];
[defaults synchronize];
NSString *timeToday = [defaults objectForKey:kTimeStampText];
NSString *dateToday = [defaults objectForKey:kDateText];
tableVC.tableDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:timeToday, #"Time", dateToday, #"Date", nil];
[tableVC.tableArray addObject:tableVC.tableDict];
[tableVC.table reloadData];
}
Here's my code in the main view controller when the button to load the table modally is pressed:
// Table loaded
- (IBAction)viewHistoryPressed:(id)sender
{
if (!self.historyViewController)
{
self.historyViewController = [[HistoryViewController alloc] initWithNibName:#"HistoryViewController" bundle:nil];
}
[self presentModalViewController:historyViewController animated:YES];
}
Here's my viewDidLoad in my table view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *time = [defaults objectForKey:kTimeStampText];
NSString *date = [defaults objectForKey:kDateText];
tableDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:time, #"Time", date, #"Date", nil];
tableArray = [[NSMutableArray alloc] initWithObjects:tableDict, nil];
}
Screenshot of the main view:
detail view:
table view:
Try initializing your NSMutableArray with:
NSMutableArray *array = [NSMutableArray array];
Now you are open to adding entries.
Let me know if this works for you.
in viewDidLoad method... please use self.tableDict and self.tableArray while allocating them. 'self' calls the getter and setter methods so it is imp.

xcode global nsmutablearray that keeps values

Did a search but cant seem to find exactly what I'm looking for
Basically I load values into a nsmutablearray in one method and then I want to access these values in another method to print them to a table
I declared the array in the app.h
NSMutableArray *clients;
Then in the app.m
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *results = [responseString JSONValue];
clients = [[NSMutableArray alloc]init];
// Loop through each entry and add clients to array
for (NSDictionary *entry in results)
{
if (![clients containsObject:[entry objectForKey:#"client"]])
{
[clients addObject:[entry objectForKey:#"client"]];
}
}
}
Now Im try to acces the clients array in another method
I have seen some suggestions to use extern in the app.h? Some sort of global variable?
Any help would be appreciated
Thanks
Take the clients array in app delegate class.declare the property,synthesizes in the app delegate class.Then in the below method write like this.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
YourApplicationDelegate *delegate = [UIApplication sharedApplication]delegate];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *results = [responseString JSONValue];
clients = [[NSMutableArray alloc]init];
// Loop through each entry and add clients to array
for (NSDictionary *entry in results)
{
if (![clients containsObject:[entry objectForKey:#"client"]])
{
[delegate.clients addObject:[entry objectForKey:#"client"]];
}
}
}
after that suppose you if you want to use the clients array in another class do like this.
YourApplicationDelegate *delegate = [UIApplication sharedApplication]delegate];
NSLog(#"client array is %#",delegate.clients);

Memory problems with NSMutableDictionary, causing NSCFDictionary memory leaks

Help me please with the following problem:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [[NSMutableDictionary dictionary] retain];
// I was trying to change this on the commented code below, but did have no effect
// NSMutableDictionary *gamesDictionary = [[NSMutableDictionary alloc] init];
// [gamesDictionary retain];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
The leak starts in another method of another class, there the getGamesList method is called, like this:
NSMutableDictionary *gamesDictionary;
gamesDictionary = [[NSMutableDictionary dictionaryWithDictionary:[appDelegate getGamesList]] retain];
After that there are a lot of leaks that points to NSCFArray in the string:
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
if ([keys count] != 0) return [[keys objectAtIndex:section] uppercaseString];
return #"";
}
I assume these things are connected to each other, but I still can not understand all of the memory management tips.
Thanks a lot!
Didn't use Cocoa for years (that's why I can't tell you an exact answer :/). But I guess your problem is that you systematically use retain on your objects.
Since the object reference count never get to 0, all dictionaries are keep in memory and not freed.
Try to remove the retain on [NSArray arrayWithArray] and [NSMutableDictionary dictionaryWithDictionary
http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_beginners/Some_Cocoa_essential_principles#Retain_and_Release
It does look like you are over-retaining your array.
When you create the gamesDictionary it is created with an retain count of +1. You then retain it (count becomes +2). When you get the value outside this function you retain again (count becomes +3).
You are correct that if you create an object you are responsible for it's memory management. Also, when you get an object from a method, you should retain it if you want to keep it around for longer than the span of the function. In your case, you just want to get at some of the properties of the object, so you don't need to retain it.
Here is a suggestion:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [NSMutableDictionary dictionary]; // Remove the retain.
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
This next bit is messy. you create a new dictionary and retain it. The original dictionary is not autoreleased, so the count isn't decremented and it always hangs around. Just assign the dictionary rather than create a new one.
NSMutableDictionary *gamesDictionary = [[appDelegate getGamesList] retain];
// Retaining it, becuase it looks like it's used elsewhere.
Now, in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *returnString;
// Don't need to retain the keys because you are only using it within the function
// and since you didn't alloc, copy or retain the array it contains, you aren't responsible for it's memory management.
NSArray *keys = [NSArray arrayWithArray:[gamesDictionary allKeys]];
if ([keys count] != 0) {
returnString = [[NSString alloc] initWithString:[[keys objectAtIndex:section] uppercaseString]];
return [returnString autorelease];
}
return #"";
}

Resources