I want to store a list of data records in a NSMutableArray for use in a UITableView. In other languages I would have used a simple 'type' structure to define the record structure but I understand the way to do this in Obj-C is to define a new class. I've done this as follows :
#interface CustSuppListItem : NSObject
#property (nonatomic, copy, readwrite) NSString *acCode;
#property (nonatomic, copy, readwrite) NSString *acCompany;
#property (nonatomic, copy, readwrite) NSString *acContact;
#property (nonatomic, assign, readwrite) double osBalBase;
#property (nonatomic, assign, readwrite) unsigned int acAccStatus;
#end
#implementation CustSuppListItem
#synthesize acCode, acCompany, acContact, osBalBase, acAccStatus;
#end
In the viewDidLoad of my UITableViewController I instantiate the array :
tableListDataArray = [[NSMutableArray alloc] init];
Once I have retrieved my data, I add it to the array as follows :
CustSuppListItem *custSuppItem = [[CustSuppListItem alloc] init];
[custSuppItem setAcCode:[jsonCustSuppRecord getStringForKey:#"acCode"]];
[custSuppItem setAcCompany:[jsonCustSuppRecord getStringForKey:#"acCompany"]];
[custSuppItem setAcContact:[jsonCustSuppRecord getStringForKey:#"acContact"]];
[custSuppItem setOsBalBase:[jsonCustSuppRecord getDoubleForKey:#"osBalBase"]];
[custSuppItem setAcAccStatus:[jsonCustSuppRecord getIntForKey:#"acAccStatus"]];
[tableListDataArray addObject:custSuppItem];
[custSuppItem release];
In my table cellForRowAtIndexPath method, I retrieve the data for the current cell as follows:
CustSuppListItem *listDataRecord = [tableListDataArray objectAtIndex:indexPath.row];
[cell.lblCompanyName setText:listDataRecord.acCompany]; // EXC_BAD_ACCESS here
[cell.lblAcCodeContact setText:[NSString stringWithFormat:#"%#, %#",
listDataRecord.acCode, listDataRecord.acContact]];
[cell.lblBalance setText:[Utils fmtNumber:listDataRecord.osBalBase withDecPlaces:2]];
[cell.lblStatus setText:[Utils exchAccStatusDesc:listDataRecord.acAccStatus]];
return cell;
In the dealloc method for the view controller I release the NSMutableArray :
[tableListDataArray release];
I'm very new to Obj-C so it would be great if somebody could confirm everything I've done so far makes sense and is in order. I am getting an intermittent EXC_BAD_ACCESS error when trying to read the acCompany property (see comment next to line) so something must not be right.
Any help appreciated,
Jonathan
All your code looks reasonable and correct to me at first glance.
A few things that I would look at are:
Confirm that cell definitely has a property lblCompanyName. If you're trying to assign to a property that doesn't exist then you will get this type of error. Have you defined a custom cell object type?
Confirm that it is always the acCompany property that is causing the EXC_BAD_ACCESS, and not just any property on the object. One way to do this would be to change the ordering of the lines in the cellForRowAtIndexPath method.
Confirm that the listDataRecord that's causing the crash is getting populated correctly in the first place. In other words, confirm that your jsonCustSuppRecord is always valid. What does jsonCustSuppRecord getStringForKey: return if the key doesn't exist in the jsonCustSuppRecord?
Set a breakpoint at this line: [tableListDataArray addObject:custSuppItem]; and examine the contents of the custSuppItem each time (this is an extension of point 3. above)
Related
I'm trying to write an app that has two scenes in it. The first page is a UITableView that will contain a list of note entries. The second scene has 2 text fields (note summary and note description). I'm entering details on the second scene and then clicking a "Save" button which saves the data:
NoteListViewController.m
- (IBAction)saveAndGoBack:(id)sender {
NSLog(#"NoteDetailViewController.m: %#", NSStringFromSelector(_cmd));
NSString * desc = [[NSString alloc]init];
NSString * detail = [[NSString alloc]init];
desc = _noteTitle.text;
detail = _noteDesc.text;
[[NoteStore sharedStore]createNoteWithDesc:desc AndDetail:detail];
[self.navigationController popViewControllerAnimated:YES];
}
NoteStore is a static sharedStore that I am saving the data into.
NoteStore.m
-(Notes *)createNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail {
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
Notes * newNote = [[Notes alloc]initNoteWithDesc:desc AndDetail:detail];
[self.privateItems addObject:newNote];
return newNote;
}
So, the note is added to an NSMutableArray called "privateItems". I confirmed that the Note object gets added properly.
*****The problem happens when I try to retrieve the Note object (desc and detail) from the privateItems array later on using an accessor method which has a public property in the NoteStore.h file called allItems (it's an NSArray readonly, nonatomic and a copy):
NoteStore.m
-(NSArray *)allItems{
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
return [self.privateItems copy];
}
Everytime I try to retrieve it, the first property (desc) comes up as nil while the second property (detail) has the data I saved in the second text field of the second scene. Why is the first field constantly coming up as nil???
Just for clarity, a Note object is declared as follows
#interface Notes : NSObject
// What are the properties of a note?
#property (nonatomic, weak) NSString * noteDesc;
#property (nonatomic, weak) NSString * noteDetail;
#property (nonatomic, weak) NSString * test;
// Designated Initializer
-(instancetype)initNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail;
#end
Any help would be greatly appreciated.
When you call the designated initialiser you pass in two NSString objects. At this point they are owned by the method where they are created.
When they are assigned to the properties they only have a weak reference and therefor the retain count is not bumped up. Weak references are good for things like delegate objects. In this case you want your objects to stick around, so by declaring them as strong you're saying I want these properties to stick around in memory and take ownership of them.
I have experience with Java and Android development, and am now attempting to learn Objective-C and iPhone/iPad development. In order to help teach myself, I am re-writing an application I made for android over to iPhone.
I am now attempting to use the UISearchBar in a tableview that I have populated with names from "member" objects. However, I am having trouble using NSPredicate to retrieve the name properties from inside the member objects that I have created, as it crashes. I was able to create a workaround by making an entirely seperate array filled with just the names and use that with NSPredicate, but this is far from ideal and creates problems down the road.
So basically by doing this I was able to pinpoint the problem to either how I use NSPedicate or maybe how I set my member objects in a previous class. Just to clarify, my object is properly filled when I do go into the method that uses NSPredicate so I know my objects are not just nil.
Here is my .h for my member class.
#interface AKPsiMember : NSObject
#define CURRENT_STATUS #"Current"
#define ALUMNI_STATUS #"Alumni"
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
#property (nonatomic, strong) NSString *emailAddress;
#property (nonatomic, strong) NSString *pledgeClass;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *phoneNum;
And also my TableViewController .m that contains the UISearchBar method delegates
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.firstName contains[c] %#",
searchText];
self.searchedMemberNameResults = [self.listedMembers filteredArrayUsingPredicate:resultPredicate];
}
And finally the error from my stack trace
'NSInvalidArgumentException', reason: '-[AKPsiMember isEqualToString:]: unrecognized selector sent to instance 0x716bbd0'
Well I feel silly. My problem was actually setting my cell.textLabel.text = to the member object instead of the string in the object. Apparently I made this change by accident some time ago when first implementing the UISearchBar and just never noticed.
My predicate is actually working correctly now! Thanks for everyone that took the time to help me out.
I tried initializing the array :
In .h file
#property (nonatomic, retain) NSArray *accounts;
In .m file :
#synthesize accounts;
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *arrList = [acAccountStore accountsWithAccountType:accountType];
// This returns array
self.accounts = [NSArray arrayWithArray:arrList]; // I tried debug after
// this and it gives me data in debugger.
// Note array List have 3 data in it.
}
Now On button click i call a method:
- (IBAction) ButtonClicked :(id) sender {
NSLog(#" data : %#",[self.accounts objectAtIndex:0]); // Breaks at this point.
// When i tried with debug it gives me (no Objective-C description available)
}
Is the initialization of array correct Or If the code is not right please let me know.
Main concern is when i do debug in viewDidLoad, the self.accounts show me proper values. But after doing the click event its empty and throws EXEC_BAD_ACCESS error.
Thanks for help in advance
hm looks fine. A couple of questions then:
Where are you calling the self.accounts = [NSArray arrayWithArray:arrList];
I assume that the array is being setup before your button is being pressed?
There's no real reason that arc should be clearing out the variable. Have you set a strong reference to it or a weak one? If you're using self. on a variable, you should have :
#property (nonatomic, strong) NSArray *accounts;
or similar to that in the .h file and then
#synthesize accounts;
in the .m file.
If you've got weak instead of strong then ARC may possibly clear the memory but it still shouldn't.
Update:
Create a property for your account store as well. I had this exact issue recently and this fixed it.
#property (nonatomic, strong) ACAccountStore *accountStore;
Original Answer
Because you're using ARC, you need to change your property declaration from
#property (nonatomic, retain) NSArray *accounts;
to:
#property (nonatomic, strong) NSArray *accounts;
With the latest LLVM compiler, you don't need to synthesize properties either. So you can remove #synthesize accounts.
You should always use defensive coding as well, so in your - buttonClicked: method, you should do:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts) {
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}
This makes sure that the pointer to the array is valid.
You can also check to make sure an item in an array exists before trying to read it by doing:
- (IBAction)buttonClicked:(id)sender {
if (self.accounts.count > 0)
NSLog(#"data: %#", [self.accounts objectAtIndex:0]);
}
}
I have an iPad app in progess but I'm having difficulty catching the selection of a row in my table view. I know this is because I haven't defined my delegate properly yet but, after 2 hours on the net, it still isn't making much sense.
What I'm trying to do is pass the selected table row item to a new view that displays info based on the selection - pretty standard.
I set up the tableViewController sub class using the option to create it as a UITableViewController subclass which, unless I am wrong, incorporates the delegates (UITableViewDelegate and UITableViewDataSource) automatically.
In the didSelectRowsAtIndex method I'm trying to create a DetailViewController. I've tried with a nib file and creating one purely in code but the class is never created. I'm missing a step I'm sure of it but I can't find what it is. At some point shouldn't I be defining what function I want to access with the selected row? But where? How?
In what I considered was my best attempt, I created the DetailViewController, set a string variable in the detailViewController to the selected row, and then tried to add the detailViewController view to display. I figured I could then use the viewDidload to call the next function but the view never got displayed on screen.
Some basic guidence would be nice. Or a decent tutorial would be nice. No calls to read the relevant docs please, I've been over it and right now I need a example to pull things together.
Thanks,
Steve
I think you are missing this line in your didSelectRowAtIndexPath: method
[self presentModalViewController:controller animated:YES];
where controller is an object of class DetailViewController
Yeah, maybe paste the code snippet will be easier to figure out what's going on here. And are those delegate(didSelectRowAtIndexPath:) methods being called correctly?
Try this,
this goes in didSelectRowAtIndexPath
MoreInfoTable *moreInfoView = [[MoreInfoTable alloc] initWithStyle:UITableViewStyleGrouped];
//in the MoreInfoTable, make properties like titles etc.
[self.navigationController pushViewController:moreInfoView animated:YES];
[moreInfoView release];
}
here's an example of an MoreInfoTable.h
#interface MoreInfoTable : UITableViewController {
NSMutableArray *moreInfo;
NSURL *getDirections;
NSURL *getWebsite;
NSMutableString *getPhoneNumber;
NSString *address;
NSString *footer;
float lat, lon;
}
-(void)goToWebsite;
-(void)goToMaps;
-(IBAction)addToFavorites:(id)sender;
-(void) callNumber;
#property (nonatomic,retain) NSURL *getDirections;
#property (nonatomic,retain) NSURL *getWebsite;
#property (nonatomic,retain) NSMutableString *getPhoneNumber;
#property (nonatomic,retain) NSString *footer;
#property (nonatomic,retain) NSString *address;
#property (readwrite) float lat;
#property (readwrite) float lon;
#end
now back in the other file in which you declare the table, you can say
MoreInfoTable *moreInfoView = [[MoreInfoTable alloc] initWithStyle:UITableViewStyleGrouped];
//in the MoreInfoTable, make properties like titles etc.
moreInfoView.title = #"TITLE!";
//etc.
[self.navigationController pushViewController:moreInfoView animated:YES];
[moreInfoView release]; //
I'm new to Obj-C and iPhone SDK. The test application I'm stock with is a color switcher containing two buttons ("Back", "Forward") and one text label. The idea is to switch between rainbow colors (background) and setting an appropriate text label in a cyclic manner.
I declared NSArray (which is to contain colors names) in RainbowViewController.h, synthesized it in RainbowViewController.h and I can't add any string into that array.
This is "h" file:
#import <UIKit/UIKit.h>
#interface RainbowViewController : UIViewController {
IBOutlet UILabel *currentColorTextLabel;
NSArray *colorsArray;
NSString *msg;
}
#property (nonatomic, retain) IBOutlet UILabel *currentColorTextLabel;
#property (nonatomic, retain) NSArray *colorsArray;
#property (nonatomic, retain) NSString *msg;
- (IBAction) pressForwardButton;
- (IBAction) pressBackButton;
#end
This is "m" file:
#import "RainbowViewController.h"
#import <Foundation/Foundation.h>
#implementation RainbowViewController
#synthesize currentColorTextLabel;
#synthesize colorsArray;
#synthesize msg;
int currentArrayIndex = 0;
colorsArray = [[NSArray alloc] init]; //here i get "Initializer element is not constant" error message
[coloursArray addObject:#"Red"]; //here I get "Expected identifier or '(' before '[' token"
[coloursArray addObject:#"Orange"];
//etc
- (IBAction) pressForwardButton {
//here I'm going to increment currentArrayIndex, set an appropriate color, and update a currentColorTextLabel based on currentArrayIndex.
}
- (IBAction) pressBackButton {
}
//auto-genereted code here
#end
I'm new to obj-c as well, but I think you need to initialize the array with objects, or use an NSMutableArray if you want to add objects after it is created.
You have the code that should go in your init method just sitting out in the middle of the file. You can't set instance variables like that.
jasongetsdown is correct. You need to instantiate the NSArray object with the objects it will contain and nil terminated.
#"Red", #"Blue", nil
If you wish to have an array that you can change you need to make it a Mutable Array.
However, you have another problem here. Your property that you are synthesizing and allocating for is an object named colorsArray and you are trying to pass a method to a coloursArray object, two different spellings.