Out of scope error when adding an object to an NSMutableArray - objective-c

I am trying to add a class object to an NSMutableArray but the object appears to be out of scope after adding it.
interface:
#import <Cocoa/Cocoa.h>
#class Person;
#interface MyDocument : NSDocument
{
NSMutableArray *employees;
IBOutlet NSTableView *raiseTableView;
}
- (IBAction)createEmployee:(id)sender;
- (IBAction)deleteSelectedEmployees:(id)sender;
#end
Part of the .m file:
#import "MyDocument.h"
#import "Person.h"
#implementation MyDocument
- (id)init
{
self = [super init];
if (self) {
employees = [[[NSMutableArray alloc] init]retain];
}
return self;
}
- (IBAction)createEmployee:(id)sender
{
Person *newEmployee = [[Person alloc] init];
[employees addObject:newEmployee];
NSLog(#"personName is: %#, expectedRaise is: %f", newEmployee.personName, newEmployee.expectedRaise);
[newEmployee release];
[raiseTableView reloadData];
}
The NSLog prints everything correctly. When I look at employees it shows 1 object added, when I look at the object it has a notation that it is out of scope and when I try to print it I get null for a result. Consequently, when it tries to reloadData things blow up. Anyone give me a hint as to what I am forgetting? Thanks.
TableView code:
#pragma mark Table view datasource methods
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tempTableView
{
return [employees count];
}
- (id)tableView:(NSTableView *)tempTableView objectValueForTableColumn:(NSTableColumn *)tempTableColumn row:(NSInteger)rowIndex
{
// What is the identifier for the column?
NSString *tempIdentifier = [tempTableColumn identifier];
// What person?
Person *tempPerson = [employees objectAtIndex:rowIndex];
// What is the value of the attribute named identifier?
return [tempPerson valueForKey:tempIdentifier];
}
- (void)tableView:(NSTableView *)tempTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tempTableColumn row:(NSInteger)rowIndex
{
NSString *tempIdentifier = [tempTableColumn identifier];
Person *tempPerson = [employees objectAtIndex:rowIndex];
// Set the value for the attribute named identifier
[tempPerson setValue:anObject forKey:tempIdentifier];
}

I think it's crashing because this line:
NSString *tempIdentifier = [tempTableColumn identifier];
is returning a null for tempIdentifier, so that the null string is getting passed to the Person class here:
NSString *tempIdentifier = [tempTableColumn identifier];
Which is causing the error message. You should print out the value of tempIdentifier to be sure.
Did you set the identity field for each column in your TableView in InterfaceBuilder?

Related

NSMutableArray resetting itself when WindowDidLoad is done

When I pass a NSMutableArray from a controller class to a NSWindowController class using #property and #synthesize I am able to use the objects of the array in the windowDidLoad method.
However, after the method is done and I click a button on the window triggerig an IBAction, the passed value is nil.
Can anyone explain me why this is happening and how I can preserve the NSMutableArray?
Here is the code:
passClass.h
#import <Foundation/Foundation.h>
#class ResultWindowController;
#interface passClass : NSObject {
#private
IBOutlet NSTextField *searchField;
ResultWindowController *resultWindowController;
}
- (IBAction)passIt:(id)sender;
#end
passClass.m
#import "passClass.h"
#import "ResultWindowController.h"
#implementation passClass
- (IBAction)passIt:(id)sender {
NSString *searchString = searchField.stringValue;
NSMutableArray array = [[NSMutableArray alloc]init];
[array addObject:searchString];
[array addObject:searchString];
if(!resultWindowController) {
resultWindowController = [[ResultWindowController alloc] initWithWindowNibName:#"ResultWindow"];
resultWindowController.array =[[NSMutableArray alloc]initWithArray:array copyItems:YES];
[resultWindowController showWindow:self];
}
}
#end
ResultWindowController.h
#import <Cocoa/Cocoa.h>
#interface ResultWindowController : NSWindowController <NSTableViewDataSource> {
IBOutlet NSTableView *resultView;
NSMutableArray *resultList;
//NSMutableArray *array;
}
- (IBAction)returnValue:(id)sender;
#property (nonatomic,strong) NSMutableArray *array;
#end
ResultWindowController.m
#import "Results.h"
#interface ResultWindowController ()
#end
#implementation ResultWindowController
//#synthesize array;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
resultList = [[NSMutableArray alloc] init];
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
for (NSInteger i = 0; i< [array count];i++)
{
Results *result = [[Results alloc]init];
result.resultName = [self.array objectAtIndex:i];
[resultList addObject:result];
[resultView reloadData];
NSLog (#"self.array: %#", self.array);
// works fine, tableview gets populated, array is correct
}
}
- (NSInteger) numberOfRowsInTableView:(NSTableView *)resultView{
return [resultList count];
}
- (id)tableView:(NSTableView *)resultView objectValueForTableColumn:(NSTableColumn *)resultColumn row:(NSInteger)row{
Results *result = [resultList objectAtIndex:row];
NSString *identifier = [resultColumn identifier];
return [result valueForKey:identifier];
}
- (IBAction)selectedSeries:(id)sender {
NSLog (#"self.array: %#", self.array);
//when I break here the array is nil
}
#end
Here is the NSLog result:
2013-12-26 10:36:49.487 MyProgram[545:303] self.array: (
"test",
"test"
)
2013-12-26 10:37:24.044 MyProgram[545:303] self.array: (null)
Try to remove NSMutableArray *array; from ResultWindowController class declaration and leave only the property declaration for it.
Or you could try initiating the array property in your ResultVWindowsContorller init class and in the - (IBAction)passIt:(id)sender just add objects to array.
I honestly can't see how this works at all unless, in -windowDidLoadNib you are expecting array to be empty.
When you synthesise a property, the default name of the instance variable that is used is prefixed by an underscore. Thus the class in your code has two instance variables, array and _array.
There are several ways to fix this. Here's what I think what you should do is delete the instance variable in your interface definition. Then you'll start getting compilation errors every for each time you use it. Fix them by using the property instead, so for example, the line
result.resultName = [array objectAtIndex:i];
in -windowDidLoadNib becomes
result.resultName = [self.array objectAtIndex:i];

Why can't I populate my controller with items?

I'm using an ItemController to provide a list of items to use in a tableview. I can't seem to populate the controller though, and I'm not sure why.
Here's the code for the controller class:
.h
#import <Foundation/Foundation.h>
#class Item;
#interface ItemController : NSObject
#property (nonatomic, copy) NSMutableArray *items;
- (NSUInteger)countOfList;
- (Item*)objectInListAtIndex:(NSUInteger)theIndex;
- (void)addItem:(Item *)item;
#end
.m
#import "ItemController.h"
#import "Item.h"
#interface ItemController ()
#end
#implementation ItemController
- (NSUInteger)countOfList {
return [self.items count];
}
- (Item *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.items objectAtIndex:theIndex];
}
- (void)addItem:(Item *)item {
[self.items addObject:item];
}
#end
Item.m
#implementation Item
-(id)initWithName:(NSString *)name{
self = [super init];
if (self) {
_name = name;
return self;
}
return nil;
}
#end
I'm using the following code to populate the list:
ItemController* controller = [[ItemController alloc] init];
for (NSString* key in raw_data) {
NSLog(key); // This outputs the keys fine
[controller addItem:[[Item alloc] initWithName:key]];
}
NSLog([NSString stringWithFormat:#"%d",[controller countOfList]]); // Always 0
You need to initialize the array in the init methond.
- (id)init {
self = [super init];
if (self) {
self.items = [[NSMutableArray alloc] init];
}
return self;
}
You need to initialize your variable items. In your init method, call self.items = [NSMutableArray new]; and also change your array property from copy to retain.
I also believe your class ItemController should be of kind UIViewController and not NSObject.
#interface ItemController : UIViewController
You don't initialise the _items instance variable anywhere, so it's always nil. The result of any integer-returning method called on nil will be 0, so you see that the count is 0.

NSMutableArray empty after created in init (with code)

#property (nonatomic, strong) NSMutableArray *authorMutableArray;
- (id)init {
self = [super init];
if (self) {
self.authorMutableArray = [[NSMutableArray alloc] initWithObjects:#"First Row", #"Second Row", nil];
for (NSString *string in authorMutableArray) {
NSLog(#"String: %#", string);
}
NSLog(#"Init in Add Model with Author count:%i", [authorMutableArray count]);
}
}
An example of accessing the property. The NSLog always shows the count as 0.
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
if (indexPath.row == [self.addModel.authorMutableArray count] - 1 ) {
NSLog(#"count of %i", [self.addModel.authorMutableArray count]);
return UITableViewCellEditingStyleInsert;
}
else {
return UITableViewCellEditingStyleDelete;
}
}
else {
return UITableViewCellEditingStyleNone;
}
}
The array I'm creating in init is not keeping its values past this method. Any reason why? The for loop will show both objects in the array. If I try to ask this after the init method is called, the array is empty.
Updated: Thank you everyone for your time and eyes. I had forgotten to return self in the init method.
Shouldn't the init method return self ?
In your class' interface file(.h file) declare like this:
#interface Your_Class_Name : UIViewController {
NSMutableArray *authorMutableArray;
}
#property (nonatomic, strong) NSMutableArray *authorMutableArray;
//i dont know why you prefered strong, chose retain and try again please
We aren't dealing with C++ or Java here, the -init method of an object MUST return a value.
This quirk actually allows for some pretty interesting stuff, e.x. the following:
-(id) init {
NSLog(#"Creating new instance of singleton object...");
#if __has_feature(objc-arc)
self = singleton_instance;
#else
[self release];
self = [singleton_instance retain];
#endif
return self;
}
It also allows for class 'posing' of a sort, allowing you to track exactly when an object of a particular class is initialized (that is too deep of a topic for this answer).

MKMapKit - EXC_BAD_ACCESS when looping through MKAnnotations

I've been stuck on this EXC_BAD_ACCESS error 2 days now. I have a reloadAnnotations method that removes all annotations before adding new annotations. Before removing the annotation this method should be checking to see if the new set contains the same location so it's not removed and re-added. But as soon as I try to trace out the current annotation title I get this error Thread 1: Program received signal: "EXC_BAD_ACCESS"
And when I view the annotation in the debugger the title property says "Invalid Summary". It must be caused by a value not being retained but I've tried everything and can't figure it out.
Why can't I log the annotation title to NSLog?
And why can't I compare each title and coords to other objects?
BrowseController.m
-(void)reloadAnnotations
{
NSMutableArray *toRemove = [NSMutableArray arrayWithCapacity:10];
for (id annotation in _mapView.annotations) {
if (annotation != _mapView.userLocation) {
//ParkAnnotation *pa = (ParkAnnotation *)annotation;
ParkAnnotation *pa = annotation;
NSLog(#"pa.title %#", pa.title); // Thread 1: Program received signal: "EXC_BAD_ACCESS"
[toRemove addObject:annotation];
}
}
// DON'T REMOVE IT IF IT'S ALREADY ON THE MAP!!!!!!
for(RKLocation *loc in locations)
{
CLLocationCoordinate2D location;
location.latitude = (double)[loc.lat doubleValue];
location.longitude = (double)[loc.lng doubleValue];
ParkAnnotation *parkAnnotation = [[ParkAnnotation alloc] initWithTitle:loc.name andCoordinate:location];
[_mapView addAnnotation:parkAnnotation];
}
[_mapView removeAnnotations:toRemove];
}
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"BrowseViewController map viewForAnnotation");
MKPinAnnotationView *pin = (MKPinAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier: #"anIdentifier"];
if (pin == nil){
pin = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier: #"anIdentifier"] autorelease];
pin.pinColor = MKPinAnnotationColorRed;
pin.animatesDrop = YES;
pin.canShowCallout = YES;
}
else{
pin.annotation = annotation;
}
return pin;
}
ParkAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface ParkAnnotation : NSObject <MKAnnotation> {
NSString *title;
CLLocationCoordinate2D coordinate;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d;
#end
ParkAnnotation.m (edited: see Wolfgangs comments below )
#import "ParkAnnotation.h"
#implementation ParkAnnotation
#synthesize title, coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
self = [super init];
if (self) {
title = ttl;
coordinate = c2d;
}
return self;
}
- (void)dealloc {
[title release];
[super dealloc];
}
#end
Although you have declared title has a copy type property, it never is copied as you don't use the setter method and directly assigned. You are even releasing it without ownership. Change it like this,
title = [ttl copy];
The initializer in ParkAnnotation.m isn't written following ObjC conventions. The self variable is never set, the designated initializer of a class should follow the following pattern:
- (id)init
{
self = [super init];
if (self)
{
/* custom initialization here ... */
}
return self;
}
Since self is not set, the accessor methods used in the caller will fail; the container object (inside ParkAnnotation.m referenced with self) will be nil or some bogus value when trying to access a property inside the object from another class.

display image cell in tableview

I tried to display imagecells in tableview, but the program crashes..
here's my imageCell class:
#interface ImageCell : NSTextFieldCell {
NSImage *imageIcon;
NSString *labelStr;
}
#property (retain) NSImage *imageIcon;
#property (retain) NSString *labelStr;
- (id) initWithLabel:(NSString *) labelString imageName:(NSString *) imageString;
when the program runs, it crashes with error message:
-[NSTextFieldCell labelStr]: unrecognized selector sent to instance 0x10011f160
but I already provide my own imageCell class...
is there anything I did wrong, thanks!!!
here's my tableview delegate & datasource:
- (void) awakeFromNib {
self.list = [[NSMutableArray alloc] init];
[self.list addObject:[[ImageCell alloc] initWithLabel:#"Library" imageName:#"album.tiff"]];
// set first cell here
NSTableColumn *col = [[self.tableView tableColumns] objectAtIndex:0];
[col setDataCell:[list objectAtIndex:0]];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)pTableView {
return [self.list count];
}
- (id)tableView:(NSTableView *)pTableView objectValueForTableColumn:(NSTableColumn *)pTableColumn row:(int)pRow {
ImageCell *imageCell = (ImageCell *)[self.list objectAtIndex:pRow];
return imageCell.labelStr;
}
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell
forTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)pRow {
ImageCell *imageCell = (ImageCell *)cell;
imageCell.image = [[self.list objectAtIndex:pRow] imageIcon];
[imageCell setTitle:imageCell.labelStr];
}
The docs don't specify any guarantee that the table columns returned by -tableColumns are in any particular order. Try asking for -tableColumnWithIdentifier: (and make sure your table columns have identifiers specified in the nib).