Cannot add object to NSMutableArray - objective-c

I've checked Google and I haven't found anything that helps.
I'm writing a stack class in Objective-C, the stack is based around an NSMutableArray, however, I cannot add any objects to it, it's not throwing any errors in the console, and there are no compile warnings/errors. Here is my code for the stack object.
#import "Stack.h"
#implementation Stack
#synthesize stack;
- (id)init {
self.stack = [[NSMutableArray alloc] init];
return self;
}
- (void)push:(id)object { [self.stack addObject:object]; }
- (int)size { return [self.stack count]; }
- (id)pop {
id obj = [[[self.stack lastObject] retain] autorelease];
[self.stack removeLastObject];
return obj;
}
- (id)peek { return [self.stack lastObject]; }
#end
Header:
#import <Cocoa/Cocoa.h>
#interface Stack : NSObject {
NSMutableArray *stack;
}
- (void)push:(id)object;
- (int)size;
- (id)pop;
- (id)peek;
#property (nonatomic, retain) NSMutableArray *stack;
#end
For the rest of the code, if I call [test_stack size], it returns zero, no matter how many times I push an object, and if I call pop or peek, it simply returns (null).
#import "TRIAL_Stack_Ctrl.h"
#implementation TRIAL_Stack_Ctrl
#synthesize test;
- (IBAction)push:(id)sender {
[test_stack push:[input stringValue]];
}
- (IBAction)pop:(id)sender {
[label setStringValue:[NSString stringWithFormat:#"%#", [test_stack pop]]];
}
- (IBAction)peek:(id)sender {
[label setStringValue:[NSString stringWithFormat:#"%#", [test_stack peek]]];
}
- (IBAction)size:(id)sender {
[label setStringValue:[NSString stringWithFormat:#"%d", [test_stack size]]];
}
#end
This leads me to believe that it's not pushing the object, is there anything I am doing wrong?

Change:
- (id)init {
self.stack = [[NSMutableArray alloc] init];
return self;
}
to:
- (id)init {
self = [super init];
if (self) {
stack = [[NSMutableArray alloc] init];
}
return self;
}

If that really is the full implementation of your TRIAL_Stack_Ctrl class, you're not assigning the test stack instance variable anywhere, so it's nil.

Apart from leaking the NSMutableArray, and unnecessary use of self.stack, it looks ok. So it seems your problem is probably in TRIAL_Stack_Ctrl class that you don not show the code to.
If you feel like you are going crazy assertions can help you get to the bottom of what is going on.
- (void)push:(id)object {
NSParameterAssert(object);
[stack addObject:object];
NSAssert([stack count], #"array is empty");
}
They compile away to nothing in release code.

This leads me to believe that it's not pushing the object, is there anything I am doing wrong?
Incorrect assumption. removeLastObject throws an NSRangeException if the mutable array has no objects in it. If you do not see a range exception, when you try to pop an empty stack, the stack itself must be nil.

Can we see the .h for the implementation? I see you are synthesizing something called 'test' but all the operations are done on something called 'test_stack'. Naming problem? If so, it should probably also be 'self.test_stack'.

Related

Where to init MutableArray?

I've tried to init/alloc it in initWithFrame but then objects wouldn't get added.
It'd only work in this method I'm calling but I call this method each time user refreshes the view so it'd init/alloc hundred times.
Not sure why it won't just work in initWithFrame.
I need to know the right way to init and add..!
-(void)queryParseMethod {
self.imageFilesArray = nil;
self.imageFilesArray = [[NSMutableArray alloc]init];
[self.imageFilesArray addObjectsFromArray:objects];
if (!error) {
for (PFObject *object in objects) {
int index = (int)[self.favArray indexOfObject:[object objectId]];
[self.imageFilesArray replaceObjectAtIndex:index withObject:object];
}
[self.favCV reloadData];
}}
Why not just:
if (self.imageFilesArray == nil) {
self.imageFilesArray = [[NSMutableArray alloc] init];
[self.imageFilesArray addObjectsFromArray:objects];
}
And make sure that imageFilesArray is a strong property.
Your most likely problem is that initWithFrame: isn't being called. If this view comes out of a storyboard, then you need to put this in awakeFromNib, since storyboard/nib-loaded objects initialize with initWithCoder:, not their designated initializer.
You generally don't want to try to do initialization in initWithCoder: because it's called too early. awakeFromNib is called after all your IBOutlets are assigned.
It is very common for experienced devs to break initialization out into its own method like this:
- (void)setup {
// Do your setup here
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setup];
}
}
- (void)awakeFromNib {
[self setup];
}
Doing it this way makes sure that the object is initialized in either case.
Another common solution is lazy initialization, particularly for things like NSMutableArray:
#interface MyView
#property (nonatomic, readonly, strong) NSMutableArray *imageFilesArray;
#end
#implementation MyView
- (NSMutableArray *)imageFilesArray {
if (_imageFilesArray == nil) {
_imageFilesArray = [NSMutableArray new];
}
return _imageFilesArray;
}

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).

Leaks Performance Tool not detecting an obvious memory leak

To give the short version of the story here, I'm trying to make an example of a memory leak and why you would want to use the leaks performance tool in the Instruments application. I need to use this method of detecting memory leaks, but the example that I've created is not creating any memory leaks that the tool detects (yes, apparently I'm failing at failing here -_-). Here's the code:
// Memory_Leak_ExampleViewController.h
// Memory Leak Example
#import <UIKit/UIKit.h>
#import "StringReturner.h"
#interface Memory_Leak_ExampleViewController : UIViewController {
IBOutlet UITextField* xTF;
IBOutlet UITextField* yTF;
IBOutlet UITextView* result;
StringReturner* sr;
}
-(IBAction)addTogether;
-(IBAction)releaseSR;
#end
// Memory_Leak_ExampleViewController.m
// Memory Leak Example
#import "Memory_Leak_ExampleViewController.h"
#implementation Memory_Leak_ExampleViewController
- (void)viewDidLoad {
[super viewDidLoad];
sr = [[StringReturner alloc] initWithFrame:self.view.frame];
}
- (IBAction)addTogether
{
[result setText:[sr returnEq:[xTF.text intValue]:[yTF.text intValue]]];
}
- (IBAction)releaseSR
{
[sr release];
}
#end
//StringReturner.h
#import <UIKit/UIKit.h>
#interface StringReturner : UIView {
NSString* string;
NSString* returnString;
}
-(NSString*)returnEq:(int)x:(int)y;
#end
// StringReturner.m
#import "StringReturner.h"
#implementation StringReturner
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (NSString*)returnEq:(int)x:(int)y
{
string = [[NSString alloc] initWithString:#""];
int result = x+y;
string = [NSString stringWithFormat:#"%d + %d = %d", x, y, result];
return string;
}
- (void)dealloc {
[super dealloc];
}
#end
All of the IBActions and IBOutlets are set up correctly, so shouldn't there be a memory leak after adding once and then releasing the instance of StringReturner? If not, what am I not doing wrong?
Initializing NSString with static strings like #"" doesn't result in a real string being allocated and put on heap so Leaks doesn't detect it. It's an optimization.
Try leaking the other string created by stringWithFormat. Instruments Leaks will pick that up right away. Put a [string retain] after string = [NSString stringWithFormat:#"%d + %d = %d", x, y, result]; to create the leak.

Having problems with adding objects to NSMutableArray

I am having problem with adding objects to NSMutableArray *array.
// Controller.m
#import "Controller.h"
#implementation Controller
- (void)parser:(NSString *)string{
[array addObject:string];
NSLog(#"answerArray(1): %#",[array objectAtIndex:1]);
[array retain];
}
#end
// Controller.h
#import <Foundation/Foundation.h>
#interface Controller : NSObject {
NSMutableArray *array;
}
- (void)parser:(NSString *)string;
#end
NSLog(#"answerArray(1): %#",[array objectAtIndex:1]);
Results: answerArray(1): (null)
First off, you're over-retaining the array.
Second, you didn't provide the code for initializing the array, so I guess it's not allocated and initialized. This will cause the code to message a nil object and thus return nil.
You should create an init method for the Controller object, and allocate a new NSMutableArray object (and retain it).
Also, a proper dealloc to release the array.
NSMutabaleArray starts at index 0
Here is the method I added to Controller class:
- (id)init {
self = [super init];
if(self){
array = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[array release];
[super dealloc];
}

Memory leak with objective-c on alloc

When I use Instruments to find memory leaks, a leak is detected on
Horaires *jour;
jour= [[Horaires alloc] init]; // memory leak reported here by Instruments
self.lundi = jour;
[jour release];
and I don't know why there is a leak at this point.
Does anyone can help me? Here's the code.
// HorairesCollection.h
#import <Foundation/Foundation.h>
#import "Horaires.h"
#interface HorairesCollection : NSObject < NSCopying > {
Horaires *lundi;
}
#property (nonatomic, retain) Horaires *lundi;
-init;
-(void)dealloc;
#end
// HorairesCollection.m
#import "HorairesCollection.h"
#implementation HorairesCollection
#synthesize lundi;
-(id)copyWithZone:(NSZone *)zone{
HorairesCollection *another = [[HorairesCollection alloc] init];
another.lundi = [lundi copyWithZone: zone];
[another autorelease];
return another;
}
-init{
self = [super init];
Horaires *jour;
jour= [[Horaires alloc] init]; // memory leak reported here by Instruments
self.lundi = jour;
[jour release];
return self;
}
- (void)dealloc {
[lundi release];
[super dealloc];
}
#end
// Horaires.h
#import <Foundation/Foundation.h>
#interface Horaires : NSObject <NSCopying>{
BOOL ferme;
BOOL h24;
NSString *h1;
}
#property (nonatomic, assign) BOOL ferme;
#property (nonatomic, assign) BOOL h24;
#property (nonatomic, retain) NSString *h1;
-init;
-(id)copyWithZone:(NSZone *)zone;
-(void)dealloc;
#end
// Horaires.m
#import "Horaires.h"
#implementation Horaires
-(BOOL) ferme {
return ferme;
}
-(void)setFerme:(BOOL)bFerme{
ferme = bFerme;
if (ferme) {
self.h1 = #"";
self.h24 = NO;
}
}
-(BOOL) h24 {
return h24;
}
-(void)setH24:(BOOL)bH24{
h24 = bH24;
if (h24) {
self.h1 = #"";
self.ferme = NO;
}
}
-(NSString *) h1 {
return h1;
}
-(void)setH1:(NSString *)horaire{
[horaire retain];
[h1 release];
h1 = horaire;
if (![h1 isEqualToString:#""]) {
self.h24 = NO;
self.ferme = NO;
}
}
-(id)copyWithZone:(NSZone *)zone{
Horaires *another = [[Horaires alloc] init];
another.ferme = self.ferme;
another.h24 = self.h24;
another.h1 = self.h1;
[another autorelease];
return another;
}
-init{
self = [super init];
return self;
}
-(void)dealloc {
[h1 release];
[super dealloc];
}
#end
You've set your property to retain and you alloc and release the variable, so from what I can see the code is okay and Instruments has given you a false warning.
I think your copyWithZone: might have a leak, though. [lundi copyWithZone:] will retain a copy of lundi but you never release it. So you need an extra release, something like this:
-(id)copyWithZone:(NSZone *)zone{
DefibHoraires *another = [[DefibHoraires alloc] init];
Horaires* makeCopy = [lundi copyWithZone: zone];
another.lundi = makeCopy;
[makeCopy release];
return another;
}
This is because copy and alloc both return retained object instances and you need to manually release them when you're finished with them. You did that correctly for your alloc'd objects but not the copy.
That init method looks ok, although it should be implemented (and typed) as
-(id)init
{
if (self = [super init])
{
...
}
return self;
}
or a similar pattern.
Your copyWithZone implementations are wrong, they need to return a retained object, so do not autorelease the returned value. But you need to release your copy of lundi, because you are using the retaining setter.
-(id)copyWithZone:(NSZone *)zone{
DefibHoraires *another = [[DefibHoraires alloc] init];
Horaires *lundiCopy = [lundi copyWithZone:zone];
another.lundi = lundiCopy;
[lundiCopy release];
return another;
}
I don't know why you return an instance of DefibHoraires here, shouldn't it be a HorairesCollection?
Maybe the wrong copyWithZone: method is responsible for the reported leak (it's a leak anyway).
One further note: It's a good defensive rule to use (copy) for NSString properties instead of (retain) to remove side effects when passing NSMutableString instead.
I don't have an answer but I do have some general comments:
In copyWithZone: you should use allocWithZone: (passing the same zone as a parameter) to allocate the object you are going to return.
copyWithZone: should return a retained object. Don't autorelease it.
You are not supposed to use properties in init. Your init should look something like:
-init
{
self = [super init];
if (self != nil)
{
lundi = [[Horaires alloc] init]; // assign the ivar directly
}
return self;
}
In your copyWithZone: for HorairesCollection you have a leak. It should look like:
-(id)copyWithZone:(NSZone *)zone{
DefibHoraires *another = [[DefibHoraires allocWithZone: zone] init];
another.lundi = [[lundi copyWithZone: zone] autorelease];
return another;
}