I have two views, my first view class1.m and second view is class2.m. My second view is initialized as a popover when a button is pressed on a toolbar in the first view. I have an array in my second view, in which objects is added, if any of the rows is pressed. I'm trying to set up a KVO in my first view, so that i can access the allSelectedFocus array from the second view in my first view, but it's not working. I realize that i don't invoke removeObserver, but i don't know where to invoke it, without it removing the observer before it's used. If anybody know any better ways to do this, I'm open for suggestions, but if someone can get this to work, that would be really awesome too.
//class2.m
#import "class2.h"
#import "class1.h"
#implementation class2
#synthesize selectedFocus = _selectedFocus;
#synthesize focusArray = _focusArray;
#synthesize allSelectedFocus = _allSelectedFocus;
- (void)viewDidLoad
{
_focusArray = [[NSArray alloc]initWithObjects:#"Balance",#"Bevægelse",#"Elementskift",#"Vejrtrækning",#"Alle",nil];
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _focusArray.count;
}
- (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];
}
NSString *cellValue = [_focusArray objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_selectedFocus = [[_focusArray objectAtIndex:indexPath.row] stringByAppendingString:#","];
if(![[self mutableAllSelectedFocus] containsObject:_selectedFocus])
{
//add object to array, if it's not already there
[[self mutableAllSelectedFocus] addObject:_selectedFocus];
}
else
{
//remove object from array, if it's already there
[[self mutableAllSelectedFocus] removeObject:_selectedFocus];
}
}
-(NSMutableArray *)allSelectedFocus
{
if(_allSelectedFocus == nil)
{
_allSelectedFocus = [[NSMutableArray alloc]init];
}
return _allSelectedFocus;
}
-(NSMutableArray *)mutableAllSelectedFocus
{
return [self mutableArrayValueForKey:#"allSelectedFocus"];
}
#end
//class1.m
#import "class1.h"
#import "class2.h"
#implementation class1
- (void)viewDidLoad
{
[super viewDidLoad];
if(_focusTag == nil)
{
_focusTag = [[class2 alloc]init];
}
[_focusTag addObserver:self forKeyPath:#"selectedFocus" options:NSKeyValueObservingOptionNew context:NULL];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"allSelectedFocus"])
{
NSLog(#"%#", [object valueForKeyPath:keyPath]);
}
}
I suspect that this is either a function of the fact that NSArray objects are not observable or some broader violation of KVC compliance. Regardless, just implement manual change notification for NSArray objects and you should be fine. I just tested changes to NSArray objects (the adding of objects) and automatic notification did not take place, but when I added manual notification, it worked fine. (Though, curiously, NSKeyValueObservingOptionOld doesn't work as expected, showing the new value instead of the old value.) FYI, here is an example of the update method which adds something to my objects NSMutableArray, using manual notification:
- (void)addToMyArray:(id)obj
{
[self willChangeValueForKey:#"myArray"];
[_myArray addObject:obj];
[self didChangeValueForKey:#"myArray"];
}
Update:
By the way, if you need NSKeyValueObservingOptionOld, you can do something like:
- (void)addToMyArray:(id)obj
{
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:_myArray];
[tempArray addObject:obj];
[self setMyArray:tempArray];
}
This way, you don't need manual notification, and you can retrieve both old and new values, but it also seems like an inefficient use of memory, so there are pros and cons.
Just a heads up, at no point are you using an accessor method to set / get your properties. This means that KVO won't work. I believe KVO relies on the getting / setting of properties via accessors.
I'm not sure of what you're trying to accomplish with the application, but I put together some code that may be of help. I commented within the code so I won't be explaining it throughout this answer.
I'll start with class2 as you did:
#import <UIKit/UIKit.h>
#interface Class2ViewController : UITableViewController
#property (nonatomic, strong) NSArray *focusArray;
#property (nonatomic, strong) NSMutableArray *allSelectedFocus;
// This is a readonly property that will return a mutable array of the allSelectedFocus property
// This gives you the ability to have automatic KVO if you add/remove using this property
// You won't have to wrap your calls to will/didChangeValueForKey:
#property (nonatomic, readonly, strong) NSMutableArray *mutableAllSelectedFocus;
#end
#import "Class2ViewController.h"
#import "Class1ViewController.h"
#implementation Class2ViewController
#synthesize focusArray = _focusArray;
#synthesize allSelectedFocus = _allSelectedFocus;
- (void)viewDidLoad
{
[super viewDidLoad];
// This is what you have
// FYI you are accessing the iVar directly, not sure if that matters in your app or not
_focusArray = [[NSArray alloc] initWithObjects:#"Balance",#"Bevægelse",#"Elementskift",#"Vejrtrækning",#"Alle",nil];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Grab the string of interest from the _focusArray --> this is direct access again which I imagine is fine
NSString *selectedFocus = [[_focusArray objectAtIndex:indexPath.row] stringByAppendingString:#","];
// Use the new mutableAllSelectedFocus property to check if the array doesn't contain the string of interest
if (![[self mutableAllSelectedFocus] containsObject:selectedFocus]) {
// If it doesn't contain it, add it using the mutableAllSelectedFocus property
[[self mutableAllSelectedFocus] addObject:selectedFocus];
}
}
// This is getter that lazily instantiates your _allSelectedFocus array
- (NSMutableArray *)allSelectedFocus
{
// Check to see if the backing iVar is nil
if (_allSelectedFocus == nil) {
// If it is, create an empty mutable array
_allSelectedFocus = [[NSMutableArray alloc] init];
}
// return the array
return _allSelectedFocus;
}
// This is our new property
- (NSMutableArray *)mutableAllSelectedFocus
{
// mutableArrayValueForKey: returns a mutable array for the given key (property)
// Allows us better KVO and efficiency with changing properties
return [self mutableArrayValueForKey:#"allSelectedFocus"];
}
And now class 1:
#import "Class1ViewController.h"
#import "Class2ViewController.h"
#implementation Class1ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// If you are using ARC, this instance will be deallocated after -viewDidLoad
// You will want to store this in an instance variable if you need to keep it around
Class2ViewController *class2ViewController = [[Class2ViewController alloc] init];
[class2ViewController addObserver:self
forKeyPath:#"allSelectedFocus"
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"allSelectedFocus"]) {
NSLog(#"%#", [object valueForKeyPath:keyPath]);
}
}
I'm not sure if this change in code will be helpful in your application. Two things I would do though would be read the Key-Value Coding and Key-Value Observing Guides if you haven't and read this post on to-many relationships and properties.
If I got something wrong, just leave a comment.
Good luck.
Related
I have a tabbed view controller and I put a table view in it but when I run the program I got this EXC_BAD_ACCESS signal.
The array does not get loaded and produces this error.
Here is my code:
ContactsViewController.h
#import <UIKit/UIKit.h>
#interface ContactsViewController : UITableViewController <UITableViewDataSource,UITableViewDelegate>
#property (nonatomic ,retain) NSArray *items;
#end
ContactsViewController.m
#import "ContactsViewController.h"
#interface ContactsViewController ()
#end
#implementation ContactsViewController
#synthesize items;
- (id)initWithStyle:(UITableViewStyle)style {
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
items = [[NSArray alloc] initWithObjects:#"item1", #"item2", "item3", nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
return cell;
}
#end
The problem with your code lies (as the debugger tells you) in the following line:
items = [[NSArray alloc] initWithObjects:#"item1", #"item2", "item3", nil];
Take a closer look at "item3". There is no # sign in front of it so it is not an NSString object but a plain old C string. You can only put objects into an NSArray.
Change it to
items = [[NSArray alloc] initWithObjects:#"item1", #"item2", #"item3", nil];
or even simpler
item = #[#"item1", #"item2", #"item3"];
Without the rest of the code it's not possible to say for sure what the problem is, but you should access the variable through the property in the following way.
self.items = ...
Also consider using the shorthand array notation, like this.
self.items = #[#"Item 1", #"Item 2", #"Item 3"];
IMO: The only time you should use variables directly is in an overriden property accessory.
Also note that if you do want to use the variable directly you should change the synthesize command to the following #synthesize items = variableName; This puts a name, variableName on the underlying variable used in the property. You can then access the variable without going through the property.
I am trying to take an array that I loaded from another viewer and put it into a UITableView, not a TableViewController. I don't know how I would do this. Right now I have this code:
CRHCommentSection.m
#import "CRHCommentSection.h"
#import "CRHViewControllerScript.h"
#interface CRHCommentSection ()
#end
#implementation CRHCommentSection
#synthesize observationTable;
NSArray *myArray;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
myArray = [CRHViewControllerScript theArray];
NSLog(#"%#", myArray);
//NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:1]];
//[observationTable insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
NSLog(#" in method 1");
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#" in method 2");
// Return the number of rows in the section.
return [myArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#" in method 3");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [myArray objectAtIndex:indexPath.row];
return cell;
}
CRHCommentSection.h
#import <UIKit/UIKit.h>
#interface CRHCommentSection : UIViewController
#property (weak, nonatomic) IBOutlet UITableView *observationTable;
#end
Since you have posted this same example code more than once, Ill give you the way to do this without using statics. And Yes you do need to set the collection (array) on the view controller not the UITableView as the tableView uses the controller as a datasource.
Lets say you had a UITableView that displayed search results... And you launch this view controller from either some other view or from the application delegate like so..
In your viewController (yours is CRHCommentSection) define a property for the array you are going to populate the table with.
#property (nonatomic,retain, setter=setSearchResults:) NSArray *searchResults; //use your array name here
//BTW please dont call it ARRAY..its confusing.
Also in your commentsController (which would be a better name that what you have) add the setter for
the array
-(void) setSearchResults:(NSArray *)searchResults
{
//in case we loaded this view with results previously release the previous results
if ( _searchResults )
{
[_searchResults release];
}
_searchResults = [searchResults retain];
[_tableView reloadData];
}
When you instantiate the new view controller with the UITableView in it - set the "array",
in your case comments in my case searchResults
_searchResultsViewController = [[SearchResultsViewController alloc] initWithNibName:#"SearchResultsViewController" bundle:nil];
//now set the array on the controller
_searchResultsViewController.searchResults = mySearchResults; //(your array)
Thats a simple way to communicate the array for the table view when you instantiate a view controller.
Also the naming conventions will help you clear things up
If you have a view controller for comments it should be
CommentsViewController
And the array if it contains comments should be called comments - for readability.
Cheers.
As for the rest of the boilerplate setting the datasource and delegate and cellForRow etc,
you got that advice on the last go around so I wont repeat it here.
#bigkm had a good point
#interface SearchResultsViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
Your view controller has to be defined properly as above with the protocols.
You need to set the data source
[self setDataSource:self];
And also add the protocol to your interface.
<UITableViewDataSource>
I'm the beginner of IOS development.
I try to use the Storyboard to produced a UITableView, But when I run this App, it appear the "Program received signal: 'the SIGKILL' ".
I built a table using Storyboard (without ARC), but there is a problem during the runtime .
Here given in the code:
#import "MainTableViewController.h"
#interface MainTableViewController ()
#property (strong, nonatomic) NSMutableArray *myArray;
#end
#implementation MainTableViewController
#synthesize myArray;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
myArray = [NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.myArray = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [myArray count];
}
- (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];
}
cell.textLabel.text = [myArray objectAtIndex:indexPath.row]; // 这儿出现异常 Program received signal: “SIGKILL”
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{ }
#end
I moving up and down on the screen, and then occurs this runtime error.
I enabled the Zombie function. I found the "myArray" is NSZombie Objects,
And then the debug continue,
and I found another error "EXC_BAD_ACCESS" at the same line.
cell.textLabel.text = [myArray objectAtIndex: indexPath.row];
The myArray still is a zombie object.
How do you solve it?
Thank you! !
You have created myArray using the arrayWithObjects: method. This method returns an object that you do not own. Since you don't own it, the object might be deallocated (turn into a zombie) the next time the program goes through its run loop. In fact, that is exactly what is happening.
If you want the object to stay around, you have to become an owner of it. You do that by retaining it. There are three ways you can retain it. You can explicitly send it a retain message:
myArray = [[NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil] retain];
or you can use the property setter method:
[self setMyArray:[NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil]];
You can use dot-syntax, which uses the property setter method “under the covers”:
self.myArray = [NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil];
The property setter method (generated by the compiler) automatically retains the object.
You put the reference directly into your instance variable instead of using the setter method. It is easy to make this mistake. The compiler can help you catch this mistake if you synthesize your property differently:
#synthesize myArray = _myArray;
If you do that, the property is stored in an instance variable named _myArray, instead of an instance variable named myArray. So you have to explicitly use the _ in front of the name to access the instance variable directly:
// COMPILE-TIME ERROR!
myArray = [NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil];
// Compiles ok
_myArray = [[NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil] retain];
// Also compiles ok
self.myArray = [NSMutableArray arrayWithObjects:#"apple",#"orange",#"bananas", nil];
If you use the _ prefix on all of your instance variables, then you know that whenever you are setting something that starts with an _, you need to worry about retaining the new value and releasing the old value.
The third way you can retain the object, and the best, is to turn on ARC (Automatic Reference Counting) for your project. Then you don't have to worry about retaining and releasing objects. The compiler will automatically retain and release the object at the right times, even if you use the instance variable directly.
If you decide not to use ARC, then you need to study Cocoa Core Competencies: “Memory Management”, until you understand the rules.
Another newbie question, just when I thought I was beginning to get a very
small handle on ios programming. Ugh! I'm following a tutoria from the
appcodeblog.com where I'm building a simple tab bar application utilizing
core data to enter, display, and search vacation destinations. I've worked
through the tutorial and have a working app, but I notice when I select the
"Show Destinations" tab I get the following error. The app seems to continue
working, but the error is logged to the console. I'm trying to debug the
issue and understand exactly what is happening, but I just don't quite
understand what is wrong. I "think" I have an issue with my
ShowDestinations.xib file where I've incorrectly hooked up my objects within
the xib. Any help is MUCH appreciated. Thanks in advance for your help and
time.
Here's the error, "CoreDataTabBarTutorial[1262:207] Failed to call designated
initializer on NSManagedObject class 'Destination'.
I'm not sure what code to provide so I've started out by showing my header
and implementation files ShowDistinationsViewController.h and
ShowDestinationsViewController.m
ShowDistinationsViewController.h
#import <UIKit/UIKit.h>
#interface SearchDestinationsViewController : UIViewController {
UISearchBar *destinationSearchBar;
UITableView *searchTableView;
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
NSArray *fetchedObjects;
}
#property (nonatomic, retain) IBOutlet UISearchBar *destinationSearchBar;
#property (nonatomic, retain) IBOutlet UITableView *searchTableView;
#property (nonatomic, retain) IBOutlet NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, retain) IBOutlet NSManagedObjectContext *managedObjectContext;
#end
ShowDestinationsViewController.m
#import "ShowDestinationsViewController.h"
#import "Destination.h"
#implementation ShowDestinationsViewController
#synthesize destinationsTableView;
#synthesize destinationsArray;
#synthesize fetchedResultsController;
#synthesize managedObjectContext;
// Not sure where the following code came from so I commented it out!!! It didn't seem to break anything when I commented it out
//- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
//{
// self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
// if (self) {
// // Custom initialization
// }
// return self;
//}
- (void)dealloc
{
[destinationsArray release];
[destinationsTableView 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
/*
// 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];
}
*/
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark -
#pragma Data Fetch from Core Data
- (void) viewWillAppear:(BOOL)animated
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Destination" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil)
{
// Handle the error.
NSLog(#"mutableFetchResults == nil");
}
[self setDestinationsArray:mutableFetchResults];
[request release];
[destinationsTableView reloadData];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [destinationsArray count];
}
// Customize the appearance of table view cells.
- (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];
}
// Configure the cell...
Destination *destination = [[Destination alloc] init];
destination = (Destination *)[destinationsArray objectAtIndex:indexPath.row];
cell.textLabel.text = destination.name;
[destination release];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
#end
The problem seems to lie in
Destination *destination = [[Destination alloc] init];
destination = (Destination *)[destinationsArray objectAtIndex:indexPath.row];
[destination release];
The first line is unnecessary: In Objective-C, Destination* is a pointer to the object, not the real object. The Destination object you want is presumably already in the array. So you don't have to create an object to point to, in the line [[Destination alloc] init], which is gone immediately at the next line. What's going on was
[[Destination alloc] init] creates an object a, destination points to a. a is retained by you.
(Destination *)[destinationsArray objectAtIndex:indexPath.row] gives you another object b, which is not retained by you. destination now points to b. No one holds a any longer.
release is sent to the object pointed to by destination, i.e., to b. This is against the retain-release rule; you should release a, not b!
So, instead, just do
Destination *destination = (Destination *)[destinationsArray objectAtIndex:indexPath.row];
without the release part.
As an advice: always run Analyze (which is available below the Build menu) when you build your project. The analyzer is designed to catch common types of errors, including yours. Correct your code so that all the analyzer warnings go away; you should always regard the analyzer warning as an error on your part.
I'm trying to create a iPhone app that uses a tab bar controller. The first tab works fine.
However, when I click the second tab in the tab bar the whole app crashes. I am trying to implement a table view in the second tab.What could be causing the crash?
Here is my code:
SecondViewController.h
#import <UIKit/UIKit.h>
#class Person;
#interface SecondViewController : UIViewController<UITableViewDelegate, UITableViewDataSource >{
UITableView *tableView;
NSArray *persons;
}
#property (nonatomic, retain) IBOutlet UITableView *tableView;
#property (nonatomic,retain ) NSArray *persons;
-(void)initPersons:(NSArray *) array;
#end
SecondViewController.m
#import "SecondViewController.h"
#import "Person.h"
#implementation SecondViewController
#synthesize tableView;
#synthesize persons;
// 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 {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
- (id)init {
if (self = [super initWithNibName:#"SecondViewController" bundle:nil]) {
//self.title = #"Slot";
UIImage* anImage = [UIImage imageNamed:#"cherry.png"];
UITabBarItem* theItem = [[UITabBarItem alloc] initWithTitle:#"table" image:anImage tag:0];
self.tabBarItem = theItem;
[theItem release];
}
return self;
}
-(void)initPersons:(NSArray *) array{
int size = [array count];
int i = 0;
NSMutableArray *aPersons = [NSMutableArray array];
while (i < size) {
Person *person = [[Person alloc]init];
NSString * name =[array objectAtIndex:i];
NSArray *chunks =[name componentsSeparatedByString: #" "];
person.firstName = [chunks objectAtIndex:0];
person.lastName = [chunks objectAtIndex:[chunks count]-1];
[aPersons addObject:person];
[person release];
i++;
}
self.persons=aPersons;
[aPersons release];
}
-(NSArray *)sortArray {
NSSortDescriptor *lastNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"lastName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSSortDescriptor *firstNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"firstName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObjects:lastNameDescriptor,
firstNameDescriptor, nil];
return [persons sortedArrayUsingDescriptors:sortDescriptors];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:
#"Amin Alrusayni", #"Berksan Ates",
#"Becca Bedell", #"Joseph Carioti",
#"Christopher Conry", #"Jeremy Dobbins", #"Timothy Fox",
#"Eric Green", #"Timothy Gruscinski", #"Daniel Gur",
#"Yousef Guzaiz", #"Tyler Herzog", #"Alicia Johnson", #"Scott Kazakis",
#"Nathan Kidwell", #"Dakota Kincer", #"Scott Moore",
#"Sean Reber", #"Michael Romeo", #"Alexander Shibble",
#"Joshua Shipley", #"Mitchell Slemc", #"Thomas Smith",
#"Christopher Wagner", #"Corey Zachrich", #"Ahmed Alalawi",
#"Abdullah Alqahtani", #"Daniel Angelis", #"Brian Bartman",
#"William Haverstock", #"Hui Hong", #"Rong Li",
#"Amitkumar Mali", #"Christian Newman", nil];
[self initPersons:array];
NSArray *sortedArray = [self sortArray];
for (Person *person in sortedArray)
{
NSString *fullName = [[person.firstName stringByAppendingString:#" "] stringByAppendingString:person.lastName];
NSLog(#"%#",fullName);
NSLog(#" ");
}
[super viewDidLoad];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
//commented out this function
/*
- (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 {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[tableView dealloc];
[super dealloc];
}
#pragma mark -
#pragma mark TableView DataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.persons count];
}
/*- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [persons objectAtIndex:row];
return cell;
}*/
#end
It would be easier to help if you post your code using markdown formatting as Moshe commented. I did notice a few things with a quick scan. I also can't tell from this what you have created in interface builder and if the UITabBarController and all outlets are properly configured. You may have other things going on besides the following but here's a start.
Make sure you release anything you retain. for example, in viewDidLoad you allocate array and never release it.
Similarly, don't release things you haven't retained. In initPersons you create the mutable array aPersons using an array constructor that returns an autoreleased object. You then have [aPersons release]. This will cause a crash b/c you are releasing an object you haven't retained.
Clean up properly in viewDidUnload and dealloc. In both of these you need to release tableView. In dealloc you have [tableView dealloc]. That should be [tableView release]
Your data source creation is overly complicated but I do see a clear problem. You are setting self.persons in initPersons which is fine. You then sort the array and store it in sortedArray. Assuming your intent is to keep the array sorted, you are currently discarding the sorted array. self.persons remains the unsorted version. While this is not a crash, I doubt this was your intent.
You need to implement tableView:cellForRowAtIndexPath: This will absolutely cause a crash if missing. That's assuming you have the tableView delegate and dataSource configured correctly. You should add the protocols UITableViewDataSource and UITableViewDelegate to the interface definition for SecondViewController so you'll get the proper compiler warnings regarding implementing required protocol methods.
– tableView:cellForRowAtIndexPath: is required method of UITableviewDatasource protocol.so enable it.be happy
if u dont want any implementaion in it then just leave it with blank definition.
{
return cell;
}