plist segment control save issue - iphone

I have a issue with this peice of code, I am trying to save some data to a plist for later use, and if the user reopens the view, it should have the current segment selected for each segment control on my view.
however when I try and save the information, it crashes on the line
[array addObject:gender];
please help :) it gives EXC_BAD_ACCESS
-(IBAction)genderSelection:(id)sender
{
if([sender selectedSegmentIndex] == kSwitchesSegmentIndex)
{
gender = #"Male";
//NSLog(gender);
}
else {
gender = #"Female";
//NSLog(gender);
}
}
-(IBAction)contactTypeSelection:(id)sender
{
if([sender selectedSegmentIndex] == kSwitchesSegmentIndex)
{
contactType = #"a";
//NSLog(gender);
}
else {
contactType = #"b";
//NSLog(gender);
}
}
-(IBAction)saveData:(id)sender
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[array removeAllObjects];
[array addObject:field1.text];
[array addObject:field2.text];
[array addObject:gender];
[array addObject:contactType];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
-(NSString *)dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [path objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
-(void)viewWillAppear:(BOOL)animated
{
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
field1.text = [array objectAtIndex:0];
field2.text = [array objectAtIndex:1];
gender = [array objectAtIndex:2];
contactType = [array objectAtIndex:3];
if([gender isEqualToString:#"Male"])
{
genderSegment.selectedSegmentIndex = 0;
}
else
{
genderSegment.selectedSegmentIndex = 1;
}
if([contactType isEqualToString:#"a"])
{
}
else
{
}
[array release];
}
}
-(IBAction)textDone:(id)sender
{
[sender resignFirstResponder];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[gender release]; //need to release
[contactType release]; //need to release
[field1 release]; //need to release
[field2 release]; //need to release
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
gender = #"Male";
contactType = #"a";
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
gender = nil; //need to release
contactType = nil; //need to release
field1 = nil ; //need to release
field2 = nil; //need to release
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

You didn't didn't initialize your gender property. You need:
- (void)viewDidLoad
{
    [super viewDidLoad];
self.gender=[[NSString alloc]init];
    self.gender = #"Male";
//so something here
    // Do any additional setup after loading the view from its nib.
}
NOTE: If you use the #synthesize compiler directive then you need to the self-dot notation everywhere to refer to the property e.g. self.gender.

Related

UITableView Crashing

I have a UITableView I add as a subview of self.view and it crashes at
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Here is my code:
interface (in CRFeedViewController.m)
#property (assign) BOOL dataIsLoaded;
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(dataIsLoaded == YES)
{
return [self.items count];
}
else {
return 1;
}
}
// Return a cell for the index path
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Cell label
cell.textLabel.text = #"Tweet";
if (cell == nil) {
cell = [[UITableViewCell alloc] init];
}
return cell;
}
- (void)getTimeLine {
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
{
if (granted == YES)
{
NSArray *arrayOfAccounts = [account accountsWithAccountType:accountType];
if ([arrayOfAccounts count] > 0)
{
ACAccount *twitterAccount = [arrayOfAccounts lastObject];
NSURL *requestURL = [NSURL URLWithString:#"http://api.twitter.com/1/statuses/home_timeline.json"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setObject:#"20" forKey:#"count"];
[parameters setObject:#"1" forKey:#"include_entities"];
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:parameters];
postRequest.account = twitterAccount;
[postRequest performRequestWithHandler: ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
self.items = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
if (self.items.count != 0) {
dataIsLoaded = YES;
[self.tableView reloadData];
}
else {
NSLog(#"No items");
}
}];
}
} else {
NSLog(#"No access");
}
}];
}
- (void) viewWillAppear:(BOOL)animated
{
[self getTimeLine];
}
- (void)viewDidLoad
{
[super viewDidLoad];
/**
* TABLE VIEW
* Add subview of the table
*/
self.items = [NSArray arrayWithObjects:#"test", nil];
CGRect tableViewRect = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITableView *tableView = [[UITableView alloc] initWithFrame:tableViewRect style:UITableViewStylePlain];
tableView.dataSource = self;
[self.view addSubview:tableView];
}
EDIT
Here is my complete code, I'm sorry I don't understand all of this yet, I am still very new.
.h
//
// CRFeedViewController.h
// Twitter
//
// Created by Cody Robertson on 6/27/13.
// Copyright (c) 2013 Cody Robertson. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <Accounts/Accounts.h>
#import <Social/Social.h>
#interface CRFeedViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) UITableView *tableView;
#property (strong, nonatomic) NSArray *items;
#end
.m
//
// CRFeedViewController.m
// Twitter
//
// Created by Cody Robertson on 6/27/13.
// Copyright (c) 2013 Cody Robertson. All rights reserved.
//
#import "CRFeedViewController.h"
#import "CRComposeViewController.h"
#import "CRSearchViewController.h"
#interface CRFeedViewController ()
#property (assign) BOOL dataIsLoaded;
- (void) composeTweet: (id) sender;
- (void) searchTweets: (id) sender;
- (void) getTimeLine;
#end
#implementation CRFeedViewController
#synthesize dataIsLoaded;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
/**
* NAV BAR
* Add icon and button to nav bar
*/
// Add Twitter Icon as Title
UIImageView *UINavTitleLogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"UINavBarLogo.png"]];
UINavTitleLogo.contentMode = UIViewContentModeScaleAspectFill;
self.navigationItem.titleView = UINavTitleLogo;
// Add Search & Compose Icon
UIImage *UISearchNavButton = [UIImage imageNamed:#"Search.png"];
UIBarButtonItem *CRSearchNavButton = [[UIBarButtonItem alloc] initWithImage:UISearchNavButton style:UIBarButtonItemStylePlain target:self action:#selector(searchTweets:)];
UIImage *UIComposeNavButton = [UIImage imageNamed:#"Compose.png"];
UIBarButtonItem *CRComposeNavButton = [[UIBarButtonItem alloc] initWithImage:UIComposeNavButton style:UIBarButtonItemStylePlain target:self action:#selector(composeTweet:)];
NSArray *UINavItems = #[CRComposeNavButton, CRSearchNavButton];
self.navigationItem.rightBarButtonItems = UINavItems;
[[UINavigationBar appearance] setTitleTextAttributes:#{
UITextAttributeTextColor: [UIColor whiteColor]
}];
/**
* TAB BAR
* Add icon and label to task bar
*/
UIImage *CRFeedTabBarIcon = [UIImage imageNamed:#"Home.png"];
UITabBarItem *CRFeedTabBarItem = [[UITabBarItem alloc] initWithTitle:#"Home" image:CRFeedTabBarIcon tag:0];
self.tabBarItem = CRFeedTabBarItem;
}
return self;
}
- (void) composeTweet:(id)sender
{
/**
* PUSH VIEW TO COMPOSE
* Load the compose view
*/
CRComposeViewController *CRCompose = [[CRComposeViewController alloc] init];
CRCompose.title = #"New Tweet";
[self.navigationController pushViewController:CRCompose animated:YES];
}
- (void) searchTweets:(id)sender
{
/**
* PUSH VIEW TO SEARCH
* Load the search view
*/
CRSearchViewController *CRSearch = [[CRSearchViewController alloc] init];
CRSearch.title = #"Search";
[self.navigationController pushViewController:CRSearch animated:YES];
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(dataIsLoaded == YES)
{
return [self.items count];
}
else {
return 1;
}
}
// Return a cell for the index path
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Cell label
cell.textLabel.text = #"Tweet";
if (cell == nil) {
cell = [[UITableViewCell alloc] init];
}
return cell;
}
- (void)getTimeLine {
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
{
if (granted == YES)
{
NSArray *arrayOfAccounts = [account accountsWithAccountType:accountType];
if ([arrayOfAccounts count] > 0)
{
ACAccount *twitterAccount = [arrayOfAccounts lastObject];
NSURL *requestURL = [NSURL URLWithString:#"http://api.twitter.com/1/statuses/home_timeline.json"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setObject:#"20" forKey:#"count"];
[parameters setObject:#"1" forKey:#"include_entities"];
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:parameters];
postRequest.account = twitterAccount;
[postRequest performRequestWithHandler: ^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
self.items = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
if (self.items.count != 0) {
dataIsLoaded = YES;
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
else {
NSLog(#"No items");
}
}];
}
} else {
NSLog(#"No access");
}
}];
}
- (void) viewWillAppear:(BOOL)animated
{
[self getTimeLine];
}
- (void)viewDidLoad
{
[super viewDidLoad];
/**
* TABLE VIEW
* Add subview of the table
*/
self.items = [NSArray arrayWithObjects:#"test", nil];
CGRect tableViewRect = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITableView *tableView = [[UITableView alloc] initWithFrame:tableViewRect style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You should call the reloadData method on the main thread:
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
Not sure if this is causing your crash, but you need to init your cells with the reuse identifier:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
I would recommend that you post the trace logged in Xcode when the app crashes. It will give a better understanding as to why your app is crashing.
Add the delegate first so those methods are ran. Don't forget to subscribe to the delegate in your .h as well.
ViewController.h
#interface ViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
#property (nonatomic, strong) NSArray *items;
// In your viewDidLoad
tableView.dataSource = self;
tableView.delegate = self;
You return 1 cell even if you don't have data. You should return 0
If the error is happening in the numberOfRows method, then the culprit is most likely:
[self.items count];
The most likely case this causes an error is if self.items does not have a count method. In your viewDidLoad, you set it to an NSArray, which does have a count method. However, there is one other place you set self.items:
self.items = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
The result of that method most likely does not have a count method, and therefore is not an NSArray, yet you are storing it in a variable that is defined as NSArray.
After that line, above, put the following code:
NSLog(#"Class: %#", NSStringFromClass([self.items class]));
If the output is not NSArray, you most likely have a programming error.
Also, it looks like you are using multiple threads, to read/write to the same variable. This is usually not a good idea without implementing some sort of thread safety on those properties.

AlAssetsLibrary issue, code works in 4.3 but not 5.0

Here's my issue, if I access this class with iOS 4.X, the code works fine.... however whenever I try to access it with iOS 5.0, I get nil values for the groups & assets. What's the best way to get this to work? I'm posting the entire class for a reference...
.h
#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import "DejViewController.h"
#class Event, Venue;
#interface SelectMediaViewController : DejViewController <UITableViewDelegate, UITableViewDataSource> {
Event *event;
Venue *venue;
UITableView *tableView;
NSMutableArray *selectedAssets;
NSMutableArray *allMedia;
ALAssetsLibrary *library;
NSMutableArray *assetGroups;
}
#property (nonatomic, retain) Event *event;
#property (nonatomic, retain) Venue *venue;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSMutableArray *allMedia;
#property (nonatomic, retain) NSMutableArray *assetGroups;
- (IBAction)continuePressed:(id)sender;
#end
.m
#import <ImageIO/ImageIO.h>
#import "SelectMediaViewController.h"
#import "CaptionAllMediaViewController.h"
#import "MediaItem.h"
#import "CLValueButton.h"
#import "SelectMediaTableViewCell.h"
#define kMediaGridSize 75
#define kMediaGridPadding 4
#define kSelectImageTag 828
#interface SelectMediaViewController(Private)
- (void)setContentForButton:(CLValueButton *)button withAsset:(ALAsset *)asset;
- (void)loadData;
#end
#implementation SelectMediaViewController
#synthesize event, venue;
#synthesize tableView;
#synthesize allMedia,assetGroups;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
selectedAssets = [[NSMutableArray alloc] init];
showNextButton = YES;
}
return self;
}
- (void)dealloc {
[tableView release];
[event release];
[venue release];
[library release];
[allMedia release];
[selectedAssets release];
[assetGroups release];
[super dealloc];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"SelectMediaViewController - viewDidLoad");
}
- (void)viewDidUnload {
NSLog(#"SelectMediaViewController - viewDidUnload");
[self setTableView:nil];
[super viewDidUnload];
}
- (void)viewDidAppear:(BOOL)animated {
NSLog(#"SelectMediaViewController - viewDidAppear");
[super viewDidAppear:animated];
[self setNavTitle:#"Select Media"];
[self loadData];
[self.tableView reloadData];
float contentOffset = self.tableView.contentSize.height - self.tableView.frame.size.height;
if (contentOffset < 0) contentOffset = 0;
[self.tableView setContentOffset:CGPointMake(0, contentOffset) animated:NO];
}
- (void)viewDidDisappear:(BOOL)animated {
NSLog(#"SelectMediaViewController - viewDidDisappear");
self.allMedia = nil;
[selectedAssets removeAllObjects];
[self.tableView reloadData];
}
- (void)loadData {
NSMutableArray *tempArray = [[NSMutableArray array] init];
library = [[ALAssetsLibrary alloc] init];
void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result != NULL) {
NSLog(#"See Asset: %#", result);
[tempArray addObject:result];
NSLog(#"assets count: %i", tempArray.count);
}
else {
NSLog(#"result nil or end of list");
}
};
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group enumerateAssetsUsingBlock:assetEnumerator];
NSLog(#"group: %#",group);
}
else {
NSLog(#"group nil or end of list");
}
if (stop) {
self.allMedia = [NSMutableArray arrayWithCapacity:[tempArray count]];
self.allMedia = tempArray;
NSLog(#"Loaded data: %d & %d", [tempArray count], [self.allMedia count]);
}
};
//ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:assetGroupEnumerator
failureBlock:^(NSError *error) {
}];
//[library release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)continuePressed:(id)sender {
if ([selectedAssets count] > 0) {
CaptionAllMediaViewController *captionVC = [[CaptionAllMediaViewController alloc] initWithNibName:nil bundle:nil];
captionVC.event = self.event;
captionVC.venue = self.venue;
// Create media items
NSMutableArray *mediaItems = [NSMutableArray arrayWithCapacity:[selectedAssets count]];
for (ALAsset *asset in selectedAssets) {
MediaItem *item = [[MediaItem alloc] init];
item.asset = asset;
NSDictionary *metadata = [[asset defaultRepresentation] metadata];
NSDictionary *gpsMeta = [metadata objectForKey:#"{GPS}"];
if (gpsMeta) {
float latitude = [[gpsMeta objectForKey:#"Latitude"] floatValue];
if ([[gpsMeta objectForKey:#"LatitudeRef"] isEqualToString:#"S"]) latitude = latitude * -1;
float longitude = [[gpsMeta objectForKey:#"Longitude"] floatValue];
if ([[gpsMeta objectForKey:#"LongitudeRef"] isEqualToString:#"W"]) longitude = longitude * -1;
item.location = CLLocationCoordinate2DMake(latitude, longitude);
}
[mediaItems addObject:item];
[item release];
}
captionVC.media = mediaItems;
[self.navigationController pushViewController:captionVC animated:YES];
[captionVC release];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Images Selected"
message:#"Please select at least one image to continue."
delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
[alert release];
}
}
- (void)imagePressed:(CLValueButton *)sender {
BOOL currentlySelected = [selectedAssets containsObject:sender.valueObject];
UIImageView *imageView = (UIImageView *)[sender viewWithTag:kSelectImageTag];
if (!currentlySelected) {
[imageView setImage:[UIImage imageNamed:#"image-select-active.png"]];
[selectedAssets addObject:sender.valueObject];
} else {
[imageView setImage:[UIImage imageNamed:#"image-select.png"]];
[selectedAssets removeObject:sender.valueObject];
}
}
#pragma Table view methods
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Getting table view count: %d", [self.allMedia count]);
if ([self.allMedia count] == 0) return 0;
return ceil([self.allMedia count] / 4.0);
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 83;
NSLog(#"return83");
}
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SelectMediaTableViewCell *cell = (SelectMediaTableViewCell *)[_tableView dequeueReusableCellWithIdentifier:#"MEDIA_CELL"];
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:#"SelectMediaTableViewCell" owner:nil options:nil] objectAtIndex:0];
// wire up selectors
[cell.image1 addTarget:self action:#selector(imagePressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.image2 addTarget:self action:#selector(imagePressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.image3 addTarget:self action:#selector(imagePressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.image4 addTarget:self action:#selector(imagePressed:) forControlEvents:UIControlEventTouchUpInside];
}
int startIndex = indexPath.row * 4;
for (int i = 0; i < 4; i++) {
ALAsset *thisAsset = (startIndex + i) < [self.allMedia count] ? [self.allMedia objectAtIndex:startIndex + i] : nil;
CLValueButton *button = nil;
switch (i) {
case 0:
button = cell.image1;
break;
case 1:
button = cell.image2;
break;
case 2:
button = cell.image3;
break;
case 3:
button = cell.image4;
break;
default:
break;
}
[self setContentForButton:button withAsset:thisAsset];
UIImageView *imageView = (UIImageView *)[button viewWithTag:kSelectImageTag];
// letse see if it's selected or not...
if ([selectedAssets containsObject:button.valueObject]) {
[imageView setImage:[UIImage imageNamed:#"image-select-active.png"]];
} else {
[imageView setImage:[UIImage imageNamed:#"image-select.png"]];
}
}
return cell;
}
- (void)setContentForButton:(CLValueButton *)button withAsset:(ALAsset *)asset {
button.hidden = asset == nil;
if (asset) {
CGImageRef image = [asset thumbnail];
[button setImage:[UIImage imageWithCGImage:image] forState:UIControlStateNormal];
}
[button setValueObject:asset];
}
#pragma -
#end
Any help would be much appreciated, I've been trying to figure this out for 3 days...
The ALAssetsLibrary page in the online documentation now says "The lifetimes of objects you get back from a library instance are tied to the lifetime of the library instance."

Memory leaks in NSMutableDictionary

My coding contains a memory leak, and somehow I can't find the leak.
Leaks points me in the direction of the way I create "ReportDetailItems"
e.g. areaContainer = [[[ReportDetailItem alloc] init] autorelease];
I've been looking at this for hours and I am at a total loss, the objects reported leaking are "ReportDetailItem", and the NSMutableDictionary contained in those objects.
Please advice.
------[ReportDetailItem.h
#interface ReportDetailItem : NSObject
{
NSNumber *total;
NSMutableDictionary *items;
}
#property (nonatomic, retain) NSNumber *total;
#property (nonatomic, retain) NSMutableDictionary *items;
- (NSString *)description;
#end
------[ReportDetailItem.m
#synthesize items, total;
- (id)init {
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:0];
DLog("Alloc: %d", [items retainCount]);
}
return self;
}
- (NSString *)description {
return #"ReportDetailItem";
}
- (void)release {
[super release];
}
- (void)dealloc {
[self.items release];
[self.total release];
items = nil;
total = nil;
[super dealloc];
}
#end
------[Leaking code
NSError *error;
NSArray *data = [self.managedObjectContext executeFetchRequest:request error:&error];
if (data == nil || [data count] == 0) {
DLog(#"No data.")
} else {
for (int i=0; i < [data count]; i++) {
TaskEntity *task = [data objectAtIndex:i];
NSString *areaKey = task.activity.project.area.title.text;
NSString *projectKey = task.activity.project.title.text;
NSString *activityKey = task.activity.title.text;
ReportDetailItem *areaContainer;
if (![dataSource objectForKey:areaKey]) {
areaContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
areaContainer = [dataSource objectForKey:areaKey];
}
areaContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [areaContainer.total intValue])];
[dataSource setObject:areaContainer forKey:areaKey];
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
projectContainer = [areaContainer.items objectForKey:projectKey];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
ReportDetailItem *activityContainer;
if (![projectContainer.items objectForKey:activityKey]) {
activityContainer = [[[ReportDetailItem alloc] init] autorelease];
} else {
activityContainer = [projectContainer.items objectForKey:activityKey];
}
activityContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [activityContainer.total intValue])];
[projectContainer.items setObject:activityContainer forKey:activityKey];
}
}
I found it, the leak was located in the way I allocated the "dataSource"
---[Leak
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = [[NSMutableDictionary alloc] init];
[self fetchData];
}
---[No leak
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
self.dataSource = dict;
[dict release];
[self fetchData];
}
I'm pretty skeptic about the two ways u assign pointers to the ReportDetailItem. Why are you trying to autorelease the object in the first place? If not try this
ReportDetailItem *projectContainer;
if (![areaContainer.items objectForKey:projectKey]) {
projectContainer = [[ReportDetailItem alloc] init];
} else {
projectContainer = [[areaContainer.items objectForKey:projectKey] retain];
}
projectContainer.total = [NSNumber numberWithInt:([task.seconds intValue] + [projectContainer.total intValue])];
[areaContainer.items setObject:projectContainer forKey:projectKey];
if(projectContainer) {
[projectContainer release];
projectContainer = nil;
}

UIPicker and send selected row data to UIView

I have written a UIPicker which is populated from a .plist. This part works fine.
What I don't know how do is once the row has been selected is to display that underlying data in another UIView.
The code in my .m file is:
#import "airlinePickerViewController.h"
#implementation airlinePickerViewController
#synthesize picker;
#synthesize airlines;
#synthesize airline;
#synthesize teleno;
- (IBAction)butonPressed:(id)sender
{
NSInteger airRow = [picker selectedRowInComponent:kAirlineComponent];
NSInteger telRow = [picker selectedRowInComponent:kTelenoComponent];
NSString *air = [self.airline objectAtIndex:airRow];
NSString *tel = [self.teleno objectAtIndex:telRow];
NSString *title = [[NSString alloc] initWithFormat:#"You selected %#.", tel];
NSString *message = [[NSString alloc] initWithFormat:#"%# is in %#", tel, air];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
[title release];
[message release];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Initialization code
}
return self;
}
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"airlinedictionary" ofType:#"plist"];
NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
self.airlines = dictionary;
[dictionary release];
NSArray *components = [self.airlines allKeys];
NSArray *sorted = [components sortedArrayUsingSelector:#selector(compare:)];
self.airline = sorted;
NSString *selectedAirline = [self.airline objectAtIndex:0];
NSArray *array = [airlines objectForKey:selectedAirline];
self.teleno = array;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (void)dealloc {
[picker release];
[airlines release];
[airline release];
[teleno release];
[super dealloc];
}
#pragma mark -
#pragma mark Picker Data Source Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if (component == kAirlineComponent)
return [self.airline count];
return [self.teleno count];
}
#pragma mark Picker Delegate Methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if (component == kAirlineComponent)
return [self.airline objectAtIndex:row];
return [self.teleno objectAtIndex:row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if (component == kAirlineComponent)
{
NSString *selectedAirline = [self.airline objectAtIndex:row];
NSArray *array = [airlines objectForKey:selectedAirline];
self.teleno = array;
[picker selectRow:0 inComponent:kTelenoComponent animated:YES];
[picker reloadComponent:kTelenoComponent];
}
}
#end
Can anyone help me get to grips with how to complete this task.
Many thanks
Dereck
Now a somehow general answer:
I assume you want to switch to a completely new View (not a subview) so the first thing you probably need is a Navigation Controller or TabBarController to facilitate pushing / switching to new Views. Before switching / pushing the new View you could assign the selected values as properties to that new View after initialization but before switching to the new View.

iphoneSDK: UINavigationController Back button is causing app to crash

My app is crashing when I move between ViewControllers. This is the sequence that causes the crash salesViewController displays confirmViewController. When I press the back button in confirmViewController to go back to salesViewController, the application crashes.
I am not sure why. Here is the code for both of the controllers.
Thanks in advance.
#import "salesViewController.h"
#implementation salesViewController
#synthesize txtCardNumber;
#synthesize txtExpires;
#synthesize txtGrandTotal;
#synthesize txtZip;
#synthesize txtEmail;
#synthesize txtCCV2;
#synthesize txtInvoice;
//#synthesize button;
#synthesize strSaleType;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
}
return self;
}
-(IBAction)btnTipCalculator:(id)sender
{
NSLog(#"I was pressed");
tipCalcViewController *tipVC = [[tipCalcViewController alloc] initWithNibName:#"tipCalcView" bundle:nil];
tipVC.delegate = self;
tipVC.passedTotal = txtGrandTotal.text;
[self presentModalViewController:tipVC animated:YES];
[tipVC release];
}
-(IBAction)btnDateSelector:(id)sender
{
NSLog(#"I was pressed");
DatePickerViewController *datePickerViewController = [[DatePickerViewController alloc] initWithNibName:#"datePickerView" bundle:nil];
datePickerViewController.delegate = self;
[self presentModalViewController:datePickerViewController animated:YES];
[datePickerViewController release];
}
-(void)datePickerViewController:(DatePickerViewController *)controller didChooseDate:(NSString *)chosenDate{
NSLog(#"Chosen Date as String: %#", chosenDate );
txtExpires.text = chosenDate;
//do processing here... for example let's set the text of the button to the chosen date
//[button setTitle: chosenDate forState: UIControlStateNormal];
[self dismissModalViewControllerAnimated:YES];
}
//callback for modal tipCalculator
-(void)tipCalcViewController:(tipCalcViewController *)controller didChooseTip:(NSString *)chosenTip
{
NSString *strNewTotal = chosenTip;
//close tip calculator
[self dismissModalViewControllerAnimated:YES];
//update GUI
txtGrandTotal.text = strNewTotal;
}
-(void)btnSubmitClicked
{
NSURL *url = [NSURL URLWithString:#"https://www.eProcessingNetwork.Com/cgi-bin/tdbe/transact.pl"];
//NSURL *url = [NSURL URLWithString:#"https://www.eprocessingnetwork.com/Reflect/Post.pl"];
ASIFormDataRequest *request = [[[ASIFormDataRequest alloc]
initWithURL:url] autorelease];
//get settings
//NSUserDefaults *options = [NSUserDefaults standardUserDefaults];
//NSString *acctNumber = [options stringForKey:#"accountNumber"];
//NSString *restrictKey = [options stringForKey:#"restrictKey"];
//uncomment for actual
//[request setPostValue:acctNumber forKey:#"ePNAccount"];
//[request setPostValue:restrictKey forKey:#"RestrictKey"];
if([strSaleType compare:#"Test"] == NSOrderedSame)
{
//FOR TESTING ONLY: UNCOMMENT ABOVE FOR ACTUAL ACCOUNT
[request setPostValue:#"080880" forKey:#"ePNAccount"];
[request setPostValue:#"yFqqXJh9Pqnugfr" forKey:#"RestrictKey"];
}
//FOR TESTING ONLY: UNCOMMENT ABOVE FOR ACTUAL ACCOUNT
[request setPostValue:#"080880" forKey:#"ePNAccount"];
[request setPostValue:#"yFqqXJh9Pqnugfr" forKey:#"RestrictKey"];
//transaction type
if([strSaleType compare:#"Sale"] == NSOrderedSame)
{
[request setPostValue:#"Sale" forKey:#"TranType"];
}
if([strSaleType compare:#"Refund"] == NSOrderedSame)
{
[request setPostValue:#"Return" forKey:#"TranType"];
}
if([strSaleType compare:#"Test"] == NSOrderedSame)
{
[request setPostValue:#"Sale" forKey:#"TranType"];
}
//request no HTML output
[request setPostValue:#"No" forKey:#"HTML"];
//needed to get transID for signature capture
[request setPostValue:#"report" forKey:#"Inv"];
//card number
[request setPostValue:txtCardNumber.text forKey:#"CardNo"];
//parse date
NSString *strDateFull = txtExpires.text;
NSString *strMonth = [strDateFull substringWithRange: NSMakeRange(0, 2)];
NSString *strYear = [strDateFull substringWithRange: NSMakeRange(3, 2)];
//expire month
[request setPostValue:strMonth forKey:#"ExpMonth"];
//expire year
[request setPostValue:strYear forKey:#"ExpYear"];
//total, send as you would write it. no dollar sign needed
[request setPostValue:txtGrandTotal.text forKey:#"Total"];
//address - this makes TBDE ignore requests with no address
[request setPostValue:#"1" forKey:#"SKIP_MISSING"];
//zip
[request setPostValue:txtZip.text forKey:#"Zip"];
//ccv2
[request setPostValue:#"CVV2Type" forKey:#"1"];
[request setPostValue:txtCCV2.text forKey:#"123"];
//email
[request setPostValue:txtEmail.text forKey:#"EMail"];
//invoice # - optional
[request setPostValue:txtInvoice.text forKey:#"Invoice"];
//blocking of course
[request start];
// get confirmation
confirmViewController *anotherViewController = [[confirmViewController alloc] initWithNibName:#"confirmView" bundle:nil];
//set properties
anotherViewController.strConfirmation = [request responseString];
anotherViewController.strCardNumber = txtCardNumber.text;
anotherViewController.strExpires = txtExpires.text;
anotherViewController.strAmount = txtGrandTotal.text;
[self.navigationController pushViewController:anotherViewController animated:YES];
//reset interface
if([anotherViewController.strApproval compare:#"""Y"] == NSOrderedSame)
{
txtCardNumber.text = #"";
txtExpires.text = #"";
txtGrandTotal.text = #"";
txtZip.text = #"";
txtCCV2.text = #"";
txtEmail.text = #"";
txtInvoice.text = #"";
}
[anotherViewController release];
/*
//display the results
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Info"
message:[request responseString]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
*/
}
/*
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//Needed to get events
[txtCardNumber setDelegate:self];
[txtExpires setDelegate:self];
[txtGrandTotal setDelegate:self];
[txtZip setDelegate:self];
[txtEmail setDelegate:self];
[txtCCV2 setDelegate:self];
[txtInvoice setDelegate:self];
//adjust title depending on sale type
if([strSaleType compare:#"Sale"] == NSOrderedSame)
{
self.title = #"Sales";
NSLog(#"Passed in sale type: %#", strSaleType );
}
if([strSaleType compare:#"Refund"] == NSOrderedSame)
{
self.title = #"Refunds";
NSLog(#"Passed here in sale type: %#", strSaleType );
}
if([strSaleType compare:#"Test"] == NSOrderedSame)
{
self.title = #"Testing";
NSLog(#"Passed in sale type: %#", strSaleType );
}
//add additional button
UIBarButtonItem *submitButton = [[UIBarButtonItem alloc] initWithTitle:#"Submit" style:UIBarButtonItemStylePlain target:self action:#selector(btnSubmitClicked)];
self.navigationItem.rightBarButtonItem = submitButton;
[submitButton release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
if(txtCardNumber != NULL)
{
[txtCardNumber release];
}
if(txtExpires != NULL)
{
[txtExpires release];
}
if(txtGrandTotal != NULL)
{
[txtGrandTotal release];
}
if(txtZip != NULL)
{
[txtZip release];
}
if(txtEmail != NULL)
{
[txtEmail release];
}
if(txtCCV2 != NULL)
{
[txtCCV2 release];
}
if(txtInvoice != NULL)
{
[txtInvoice release];
}
if(strSaleType != NULL)
{
[strSaleType release];
}
[super dealloc];
}
#end
#import "confirmViewController.h"
#implementation confirmViewController
#synthesize lblStatus;
#synthesize lblCardType;
#synthesize lblCardNumber;
#synthesize lblExpires;
#synthesize lblAmount;
#synthesize lblApproval;
#synthesize strConfirmation;
#synthesize strCardNumber;
#synthesize strExpires;
#synthesize strAmount;
#synthesize strApproval;
-(void)btnSignatureClicked
{
sigCaptureViewController *anotherViewController = [[sigCaptureViewController alloc] initWithNibName:#"sigCaptureView" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
//prepare confirmation, all that is needed is the first string
NSArray *strings = [strConfirmation componentsSeparatedByString: #","];
NSString *strPreParsed = [strings objectAtIndex:0];
//break out yes/no so we can set status
//NSString *strYesNO = [strPreParsed substringToIndex:2];
NSString *strYesOrNo = [strPreParsed substringWithRange: NSMakeRange(1, 1)];
//save approval for later
strApproval = strYesOrNo;
//debug
NSLog(#"strNo= %#",strYesOrNo);
NSLog(#"strPreParsed= %#", strPreParsed);
//set results
if([strYesOrNo compare:#"Y"] == NSOrderedSame)
{
lblStatus.text = #"Approved";
lblStatus.textColor = [UIColor greenColor];
}
if([strYesOrNo compare:#"N"] == NSOrderedSame)
{
lblStatus.text = #"Declined";
lblStatus.textColor = [UIColor redColor];
}
if([strYesOrNo compare:#"U"] == NSOrderedSame)
{
lblStatus.text = #"Try Again";
lblStatus.textColor = [UIColor redColor];
}
//set card type
if([lblCardNumber.text compare:#"4"] == NSOrderedSame)
{
lblCardType.text = #"Visa";
}
if([lblCardNumber.text compare:#"5"] == NSOrderedSame)
{
lblCardType.text = #"Master";
}
if([lblCardNumber.text compare:#"6"] == NSOrderedSame)
{
lblCardType.text = #"Discover";
}
//set cardnumber
lblCardNumber.text = strCardNumber;
//set expires
lblExpires.text = strExpires;
//set amount
lblAmount.text = strAmount;
//set approval string
lblApproval.text = strPreParsed;
//add signature button
UIBarButtonItem *signatureButton = [[UIBarButtonItem alloc] initWithTitle:#"Signature" style:UIBarButtonItemStylePlain target:self action:#selector(btnSignatureClicked)];
self.navigationItem.rightBarButtonItem = signatureButton;
[signatureButton release];
//set title
self.title = #"Approval";
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
if(lblCardType != NULL)
{
[lblCardType release];
}
if(lblCardNumber != NULL)
{
[lblCardNumber release];
}
if(lblExpires != NULL)
{
[lblExpires release];
}
if(lblAmount != NULL)
{
[lblAmount release];
}
if(lblApproval != NULL)
{
[lblApproval release];
}
if(lblStatus != NULL)
{
[lblStatus release];
}
if(strConfirmation != NULL)
{
[strConfirmation release];
}
if(strCardNumber != NULL)
{
[strCardNumber release];
}
if(strExpires != NULL)
{
[strExpires release];
}
if(strAmount != NULL)
{
[strAmount release];
}
if(strApproval != NULL)
{
[strApproval release];
}
[super dealloc];
}
#end
I think you are releasing properties that should not be released, for instance you create strYesOrNo like this:
NSString *strYesOrNo = [strPreParsed substringWithRange: NSMakeRange(1, 1)];
without allocating the string. So the string belongs to the viewDidLoad function and will be released by this function. But after creating the strYesOrNo you assign it to a class property, like this:
strApproval = strYesOrNo;
By the time you dealloc your viewcontroller you try to release strApproval, but the viewDidLoad allready released this value and you get a bad-access. You can solve this problem by allocating the memory for strApproval like:
strApproval = [[NSString alloc] initWithString:strYesOrNo];
I did not go through all your code so maybe this is also true for some other properties. Hope this helped.