NSMutableArray empty after created in init (with code) - objective-c

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

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 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];

Out of scope error when adding an object to an NSMutableArray

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?

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;
}

Cannot add object to NSMutableArray

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