Memory issue in iPhone OS (shorter version) - iphone

I'm facing some sort of memory related issue I cannot figure out.
I have one summary view that lists some settings and a detail view where you can edit each setting.
In the summary view I have a tableview populated by an array (settingArray) of settings which in turn is loaded from a core data repository.
// SettingsViewController.h
#interface SettingsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSMutableArray *settingArray;
}
#property (nonatomic, retain) NSMutableArray *settingArray;
// SettingsViewController.m
- (void)viewWillAppear:(BOOL)animated {
[self setSettingArray:[DataHelper searchObjectsInContext:#"Sections" :nil :#"Id" :YES :managedObjectContext]];
}
This array of NSManaged objects is used to assign values to a custom UITableViewCell which has two labels: title and description.
// SettingsViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[…]
[cell.titleLabel setText:[[settingArray objectAtIndex:indexPath.row] valueForKey:#"Title"]];
[cell.descriptionLabel setText:[[settingArray objectAtIndex:indexPath.row] valueForKey:#"Description"]];
[…]
}
When the user clicks on a row, this loads a detail view where you can edit the values and save them to the database. The summary view passes the detail view an NSManagedObject reference (settingObject) in order to know what record in the core data database was selected and must be updated.
// SettingsViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (settingsDetailViewController == nil) {
settingsDetailViewController = [[SettingsDetailViewController alloc] initWithNibName:#"SettingsDetailView" bundle:nil];
}
// Pass corresponding settingArray object to detail view in the settingObject variable
settingsDetailViewController.settingObject = [settingArray objectAtIndex:indexPath.row];
[…]
}
In the detail view, the user can modify some values and then save the modified core data object in the database.
// SettingsDetailViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Update current setting in database
[self.settingObject setValue:[tableView cellForRowAtIndexPath:indexPath].textLabel.text forKey:#"Description"];
}
The problem is that once the value is updated in the detail view in this NSManagedObject, when the tableview information in the summary view is reloaded, I get a EXC_BAD_ACCESS exactly in the point where the label information is read from the settingArray.
// SettingsViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[cell.titleLabel setText:[[settingArray objectAtIndex:indexPath.row] valueForKey:#"Title"]];
// HERE IS WHERE THE ERROR OCCURS IN THE SECOND PASS
[cell.descriptionLabel setText:[[settingArray objectAtIndex:indexPath.row] valueForKey:#"Description"]];
[…]
}
I guess it's related to the release of the settingArray or settingObject being used, but I tried different approaches and no solution yet. All variables are declared in the corresponding .h, properties are added using nontoxic and retain, the accessors are synthesized in the .m and the objects are released in the dealloc function. According to the Cocoa memory management guidelines it should work.
The funny thing is that other parts of my code use identical arrays and pass identical objects with no problems whatsoever.
Any hints please?

Clang static analysis finds many reference counting errors, so you might want to give it a try.
You can either run it manually, using Run -> Build and Analyze, or you can enable it for all builds via the "Run Static Analyzer" build setting.
Note that this tool presently only supports C and Objective-C, but not C++ / Objective-C++.

It's hard to pinpoint the problem without the complete source.
Some items you can try:
Make sure the "settingObject" property in SettingsDetailViewController.h is declared as a "retain". Maybe you forgot the "retain", but released this instance in the dealloc method?
Try not setting the settingObject property in the SettingsDetailViewController (and not using it in any method - just comment out those sections). If that makes a difference, it must have to do with SettingsDetailViewController's retain/release of this settingObject
Trace the "retainCount" of the settingObject object in SettingsDetailViewController (debugger or NSLog statements) where you use it and after you release it in dealloc. That might give you some clues

Related

iOS 7 UITableView indexpath row property is nil

I have a UITableView and it contains a custom UITableViewCell. To test, I have an array that has three strings in it. The UITableView delegate methods are called as expected, however, the tableView:cellForRowAtIndexPath delegate is always passed an NSIndexPath instance whose row property is always == nil:
tableView:cellForRowAtIndexPath is called 3 times (once for each object in my array). I added the tableView from within the designer (storyboard) and created an outlet for it. The UITableViewCell instances appear to be correctly instantiated which each call to this delegate. I just can't wrap my head around why the [indexPath row] value is always nil.
Interface(s):
In the implmentation file:
#interface FirstViewController ()
#property(nonatomic, strong)AppDelegate *sharedDelegate;
#property(nonatomic, strong)NSArray *userList;
#end
In the header:
#interface FirstViewController : UITableViewController <FacebookDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
Init the custom cell:
-(void)viewDidLoad
{
[self.tableView registerClass: [ListCategoryCell class]forCellReuseIdentifier:#"ListCategoryCell"];
self.userList = #[#"d", #"g", #"f"]; // make some test data
}
And the delegate this is driving me mad:
//NSIndexPath.row is nil ?!
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"ListCategoryCell";
ListCategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = (ListCategoryCell *)[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell.titleLabel.text = [self.userList objectAtIndex:[indexPath row]];
cell.detailLabel.text = #"Detail";
return cell;
}
Am I missing something? Thanks!
Working Now
I left out some context (and I should not have) that I believe was very relevant to my problem. I created a UIViewController originally and then added a UITableView to this as the view. In the UITableView I created a custom prototype cell. I did all the house work:
UIViewController implemented the UITableViewDelegate & UITableViewDatasource.
Created and outlet for the UITableView.
Hooked up all the outlets
Everything seemed to work except for the fact that indextPath.row property was always nil. Some resources I found suggested that custom cells were not visible before the uitableview delegates were called.
In the end I made my class a subclass of UITableViewController. Things started working. I am still curious why my original attempt was failing.
Thanks for everyone's time. Some great comments helped me investigate some topics that are "good to know".
You need to provide at least two methods in view controller if you want it to manage your table view. They are:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
You've already provided the second, so your table view actually can produce cells but it doesn't know how many.The default value the first method returns is nil.That is the reason why you don't even have an index path.
Optionally:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
The default value is one,so you don't need to override this in case you have only one section
Make sure your view controller also follows delegate and datasource protocols.

iphone development: table view returns empty table

I am brand new to iOS development, and I could not find a solution on here or Google, so I'm asking out of desperation.
I have a class "ViewController" that is a subclass of UIViewController. In here, I have:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([self.bookTitle.text length] > 0)
self.entries = [self.bookLibrary searchForBook:self.bookTitle.text];
if ([segue.identifier isEqualToString: #"BookList"]) {
TableViewController *controller = (TableViewController *)segue.destinationViewController;
controller.itemCounter = [self.entries count];
controller.bookLibrary = [self.entries allValues];
}
}
The view for this on the Storyboard has a connection to a Table View Controller that I dragged and dropped onto the grid. I clicked the "Table View Controller" at the bottom, and set my custom class "TableViewController" in the custom class input box.
Now, from what I understand, the method above is passing all the data properly to the TableViewController.
Here's one of the methods I have in the TableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"BookCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
Book* book = [self.bookLibrary objectAtIndex:indexPath.row];
cell.textLabel.text = book.title;
NSLog(#"%#", book.title);
return cell;
}
The NSLog entry is printing out all the book titles to the console, so I know for a fact the data is being passed. However, when I run the program and click the button to pull up the Table View, it's just an empty table. Any hints? I can upload my entire project. Been at this for several hours and a bit frustrated. Please help :(
EDIT: A response suggested I look at the state of my data variables in the table methods. It suggests their state is not what I think it is and that I should use NSLog to print out their values. I did just that, and I can see all the values printed out. I don't understand... they do infact have values assigned to them. The problem isn't that the data is missing.
Make sure you're either using a UITableViewController subclass as your VC (if you're using a UITableViewController ui object from the pallet), or that you're properly hooking up the UITableView's delegate and datasource properties to your VC (if you're using a plain UIViewController object and subclass).
(see comments on question).
Try this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"bookLibrary.count %d", bookLibrary.count);
return [bookLibrary count];
}
You'll find that things are not what you think they are... Add in an implementation of viewDidLoad and viewWillAppear along with that, each with their own "I'm here" NSLog statement, and trace the flow of that second view controller appearing. Again, you'll find you've got some sequencing issues where the flow isn't working quite the way you might be assuming.
Added comment:
Ultimately, the origin of your problem is this line in your "sending" controller's prepare for segue method:
controller.bookLibrary = [self.entries allValues];
What is this doing? It's calling allValues on the Dictionary object. That method generates a new array (!) containing the values. You don't store that new Array object in any permanent storage. You just use it to set:
controller.bookLibrary = ...
So, right after that statement executes, you have:
an Array object in your prepareForSegue method (where the code is executing) that you've only stored in one variable/holder, which is:
a weak pointer to that object over in your destination view controller (TableViewController)
The method ends.
The Array returned by [... allValues] is not being held on to by anything in the Source view controller, so the only thing holding it from being garbage collected is the pointer to it in the Destination view controller.
But that pointer is a weak pointer. By definition, if that's the only pointer to an object, the pointer will be set to nil and the object released for garbage collection. Poof! No more array object, and you're left holding a nil pointer.
As you discovered, setting the "receiver" to strong lets it hold on to that Array object, even after the other code exits and it's the only pointer to the Array.
(And, your code isn't being invoked twice. If you look closely at the logging -- or better yet set a breakpoint inside the table get-row-count method -- you'll see it's only being called once. The earlier logging of "I have 8 objects" is happening over in other code, not in your TableViewController.)

Using a Table Cell's Text to Access Corresponding Object

Currently, I have a savedWorkout class that is simply a Table View populated with different exercises in each cell. My goal now is for the user to be able to click on each individual exercise, which will take you to a new view filled with detailed information about it.
For this, I have created an Exercise class that will hold the detailed information about the new object. Is this possible?
Here is some pseudo-code I have written up:
if (Table View Cell's Text == ExerciseObject.exerciseName) {
Populate a view with the corresponding information;
}
Being new to iPhone programming, I'm not exactly sure what would be the best way to do this, and this is what i'm thinking would be the best way to go about it.
My Exercise class holds an NSString to keep track of the exercise name, and three NSMutableArray's to hold different information.
Please let me know if I am going in the right direction.
EDIT:
After trying to implement my pseudo-code this is what I came up with:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Exercise *exerciseView = [[Exercise alloc] initWithNibName:#"Exercise" bundle:nil]; //Makes new exercise object.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *str = cell.textLabel.text; // Retrieves the string of the selected cell.
exerciseView.exerciseName.text = str;
[self presentModalViewController:exerciseView animated:YES];
}
However, this doesn't seem to work. When the new view is presented, the label doesn't show up (I connected the UILabel exerciseName to my desired string). Am I implementing this wrong?
Yes, of course it's possible. Just use the delegate method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
and check your data source cell based on the index location.
You might need to post your cellForRowAtIndexPath method. If done traditionally, it uses the indexPath.row to access an array of exercises to get a particular exercise, then changes cell properties based on the particular exercise. Is that about right?
It so, then you're halfway home.
EDIT
1) Use the code inside your cellForRowAtIndex path to init your str, as indicated here.
2) The new view controller view hasn't been built yet. You can't initialize a subview in the view hierarchy before the VC is ready. You need to pass the string to a property in that view controller (in a custom init method if you want), then on that class's viewDidLoad, you can set the exerciseName field to the string property you saved earlier. That subview shouldn't be part of the classes public interface.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// There should be an array of exercises, the same one used in cellForRowAtIndexPath:
NSString *str = [self.myArrayOfExercises objectAtIndex:indexPath.row];
// Just made code up here, but however you get a string to place in the cell
// in cellForRowAtIndexPath do that same thing here.
Exercise *exerciseView = [[Exercise alloc] initWithNibName:#"Exercise" bundle:nil];
// Might be wise to rename this ExerciseViewController, since it's probably (hopefully) a ViewController subclass
// no need to get a table cell, you have the info you need from your exercise array
//UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//NSString *str = cell.textLabel.text; // Retrieves the string of the selected cell.
exerciseView.exerciseName.text = str;
[self presentModalViewController:exerciseView animated:YES];
}

Get hidden value from Cell in TableView

I want to populate a TableView with data from a database and, when tap a cell, in didSelectRowAtIndexPath, sending selected ID to a new View.
I would like to view, in cell text, the description of the record and to put ID in a hidden field/property of the cell, but I could not find a way to do it.
I know I can get an ID from the data source (something like [my_data_source objectAtIndex:indexPath.row]; ) but I don't really like this way, I would prefer to have an ID assigned to a single cell.
Is there a way to do it?
Thanks in advance and greetings.
I'm guessing you've come from web development? I also found it difficult to do it this way, but its the best way. IT probably is possible - but its better if you get used to doing it like this, it really is.
Basically define an NSArray in the .h file (so the whole script can use it).
then in the init function:
// set the array
myArray = [NSArray arrayWithObjects:#"One",#"Two",#"Threee",nil];
[myArray retain];
then the table view delegate methods:
// set numebr of rows
- (CGFloat)tableView:(UITableView *)tableView numberOfRowsForSection:(NSUInteger)section {
return [myArray count];
}
// set the cell titleLabel value
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// set the cell - I can't remember the exact code then do:
cell.textLabel.text = [myArray objectAtIndex:[indexPath row]];
}
// similarly
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%#",[myArray objectAtIndex:[indexPath row]];
}
(disclaimer: these are just off the top of my head, and I haven't checked the exact methods - they're probably wrong in some way - but the code inside the functions is what you want really.)
When you've started using this you'll see its so much better than "hiding" an id somewhere in a table. To get things from the database I would suggest adding it all to a dictionary, or an array or similar and doing it like that when you init the class, but if you really want to do it dynamically then pretend your "hidden" ids are just index's of an array. So id#1 is at index 1 in your array. :)
Allright here is a quick hack on a different approach. I always like to deal with objects that are self contained. Pack all the data that you want into a custom class (here called MyData) for the table view you initialise it with the minimal amout that you need there. id and text that you pulled from the database. You also implement a function that can load the rest of the data from the DB.
When the item gets selected you pass the instance of your object to the subview controller and fill its data from the database. You can trigger the filling in the main viewcontroller or the subviewcontroller, that does not matter.
The main point is to pack all the data that goes together into one object (basically a "model" you already have a view and controller) and then fill views by accessing that object. This keeps your interface the same all the way through your applications. And makes changes easier. For example if you find out that it is better to fill in all the data from the DB at the start of your program you can do that now without changing the other views.
#interface MyObject : NSObject
{
}
// Create a stump object that contains only the necessary info
+ (id) withName:(NSString)name id:(int)id;
// loads the rest of your data from the DB
- (void) fillFromDb;
#property (readwrite, retain) NSString name;
#property (readwrite, assign) int id;
// The data fields that you need
#end
// in tableview controller
#interface MyTableViewController ...
{
NSMutableArray _dbData;
}
#end
#implementation MyTableViewController
- (void) viewDidLoad {
// Load your data from DB
for (int i =0; i < dbCount; ++i)
{
MyObject* data = [MyObject withName:dbName[i] id:dbId[i];
[_dbData addObject:data];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textLabel.text = [_dbData objectAtIndex:[indexPath row]];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Create Subviewcontroller
// Optional call fillFromDb here and not in subviewcontroller
subviewcontroller.dbData = [_dbData objectAtIndex:[indexPath row]];
//activate subview
}
#interface SubViewController {
MyObject* _dbData;
}
#end
#implementation SubViewController
- (void) viewDidLoad {
[_dbData fillFromDb];
// Do View Initialisations with the newly fetched Data
}
The code is here just to demonstrate the architecture

listing of all methods of a control - like required - iPhone

yesterday I just asked following question.
How to customize tableView Section View - iPhone
I found some new method.
Even in apple documentation I didn't found this method.
Is it some thing like hidden methods?
Does anybody provides all methods listing?
Including sample code.
Say for example.
UITableView methods
Whenever I insert tableView in my view Controller.
I have to either type or copy from some where.
If I want to include picker I have to find out UIPicker methods,
sameway
Alertview, ActionSheet, Tab Bar Controller all has different methods.
Isn't it possible, like if we include A tableView in our ViewController, Automatically all tableview methods are added to .m file.
(For example, A navigation based application has all tableView methods in rootview controller by default)
Let Me Clarify Again,
"I need proper source where all methods (like rootview controller has almost all table methods) "
So, when ever I want to add any control I just copy the code & add to my Project.
The reason Behind this
"We can target on the work instead of finding proper methods & typing them."
See, Suppose If I add a Table View to my View Controller, I must have the methods like ..didSelectAtRow..,..CellForRow...,etc.
So, after adding tableView - for managing table view I have to go for finding methods & type them in my .m file.
Suppose, I add tableView. All methods should be added to my .m file as given below.
<pre>
pragma mark Table view methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
return cell;
}
// Override to support row selection in the table view.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here -- for example, create and push another view controller.
// AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:#"AnotherView" bundle:nil];
// [self.navigationController pushViewController:anotherViewController animated:YES];
// [anotherViewController release];
}
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
I provided the answer to the question you mentioned - it definitely is in the Apple documentation (although as you say, not in the sample file). Remember the method name is
tableview:didSelectRowAtIndexPath
if you miss off the "tableview:" bit at the beginning and just search for
didSelectRowAtIndexPath
you won't find it in the documentation so easily.
If you look in the documentation that comes with XCode, you will see, for example, all methods that you can implement for the UITableview Delegate, including the one I posted to your previous answer. Just type "UITableview" into XCode help, and then select "UITableview delegate". It will then display all the methods available for you to call, and you can even just copy and paste them straight into your code.
I don't know if anyone's already done this and made the "template" classes you ask about available, but it should be very easy for you to do this yourself if you want.
Hope that helps
Sure; implementors of classes are free to implement any number of methods as a part of a class's internal implementation.
But that doesn't mean that you should use them.
You can use the Objective-C runtime's API for figuring out all the methods and classes, including those that aren't publicly declared.
But don't bother.
Specifically, if a method is not declared in the provided header files and is not documented in the documentation, don't use it. Using such a method will lead to fragility and maintenance headaches; your app may likely break at the next software update.
On the iPhone, your are expressly directed not to use private interfaces and your app will run the risk of rejection if you do so.
But I don't think that is what you are really asking. You mention:
Say for example. UITableView methods
includes
didSelectRowAtIndexPath
cellForRowAtIndex Path
numberOfSectionsInTableView
titleForHeaderInSection
However, UITableView does not declare any of those methods. Instead, it declares:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
Or, succinctly, tableView:didSelectRowAtIndexPath:, etc...
The methods you describe are in the documentation. They're in UITableViewDelegate and UITableViewDataSource, which are described at the top of the UITableView documentation. You can easily copy and paste the method definitions you need from the documentation. You can also find the protocol definition in the headers easily by using "File>Open Quickly..." and typing in the name of the protocol ("UITableViewDelegate" for instance). They are often written in the headers to make it easy to copy and paste what you need most often.
This is sometimes a small hassle in Cocoa, because Xcode doesn't auto-complete method signatures. It would save a little trouble if it did. But the solution is not to implement every delegate method that exists (as #bbum pointed out earlier). In the vast majority of cases, only a small fraction of the possible delegate methods are ever implemented. So automatically populating them all would cause much more work than it saved.