Recommend class design in Objective-C - objective-c

I'm new to Objective-c. For learning purposes I'm trying to build something like a phonebook. So I'll have a class called Person that will have some properties (name, phone, etc).
Right now I'm not preoccupied about persistence. But, I need something to "hold" Person objects. So I thought about create a class called People, but I don't know how to design it, specially the NSMutableArray that will hold the objects.
What I did was:
PERSON.H
#interface Person : NSObject {
NSString *name;
}
#property(readwrite, copy) NSString *name;
#end
PERSON.M
#implementation Person
#synthesize name;
#end
PEOPLE.H
#interface People : NSObject {
NSMutableArray *peopleArray;
}
#property(readwrite, retain) NSMutableArray *peopleArray;
- (void)addPerson:(Person *)objPerson;
#end
PEOPLE.M
#implementation People
#synthesize peopleArray;
- (id)init {
if (![super init]) {
return nil;
}
peopleArray = [[NSMutableArray alloc] retain];
return self;
}
- (void)addPerson:(Person *)objPerson {
[peopleArray addObject:objPerson];
}
PHONEBOOK.M
...
Person *pOne = [[Person alloc] init];
pOne.name =#"JaneDoe";
People *people = [[People alloc] init];
[people addPerson:pOne];
When I try to use this code, I receive an error:_method sent to an uninitialized mutable array object.
So, since I'm a newbie, probably the way that I did isn't the best/correct one. So, how do I do this?

Two things wrong with your initialiser for people. It should look more like this:
- (id)init {
self = [super init]; // always assign the result of [super init] to self.
if (!self) {
return nil;
}
peopleArray = [[NSMutableArray alloc] init]; // use init not retain.
return self;
}

Because you're not calling init on the NSMutableArray when you create your peopleArray. Try calling:
peopleArray = [[NSMutableArray alloc] init];
instead. You need not retain it unless you say for instance, did this:
peopleArray = [[NSMutableArray array] retain];
For reasons why, see the rules for memory management. These rules are very important for any iPhone or Mac developer to understand, and quite frankly, are simple enough that there's no excuse. :)

In People.m you probably meant to write
peopleArray = [[NSMutableArray alloc] init];
instead of
peopleArray = [[NSMutableArray alloc] retain];

Related

Singleton How to Declare NSMutableArray

I am trying to implement a singleton which contains an NSMutableArray* what I've done so far is below.
Singleton.h
#import <Foundation/Foundation.h>
#interface Singleton : NSObject{
NSMutableArray* ClassArray;
}
#property NSMutableArray* ClassArray;
+(Singleton*) getInstance;
- (void) SetClassArray:(NSMutableArray *)InputClassArray;
- (NSMutableArray*) GetClassArray;
Singleton.m
#import "Singleton.h"
#implementation Singleton
#synthesize ClassArray;
static Singleton *singletonInstance;
+ (Singleton*)getInstance{
if (singletonInstance == nil) {
singletonInstance = [[super alloc] init];
}
return singletonInstance;
}
- (void) SetClassArray:(NSMutableArray *)InputClassArray{
ClassArray = InputClassArray;
}
- (NSMutableArray*) GetClassArray{
return ClassArray;
}
#end
what I'm wondering is where does [[NSMutableArray alloc]init]; go? or do i not need it. Also when ClassArray is initialised i want some default values, (the return from another function,[DatabaseFunctions GetClassDefaultArray] ) again where does this go?. I'm assuming the line below singletonInstance = [[super alloc] init]; however it doesn't accept ClassArray.
I have an application consisting of four view controllers, what I need is an nsmutablearray that can be accessed and written to from any of these views. There are three others in total.
Thanks
I'm wondering is where does [[NSMutableArray alloc]init]; go? or do i not need it
That depends on what you expect in your getter. Is reasonable for your use case to return nil in GetClassArray?
You could guard against it by returning an empty array if it is nil:
- (NSMutableArray*) GetClassArray{
if ( ! ClassArray ) {
ClassArray = [[NSMutableArray alloc] init];
}
return ClassArray;
}
i want some default values
You can populate your array as you see fit whenever you want, the best place to start would probably be your init method:
+ (Singleton*)getInstance{
if (singletonInstance == nil) {
singletonInstance = [[super alloc] init];
NSMutableArray *array = ...;
[singletoneinstance SetClassArray:array];
}
return singletonInstance;
}
As a side note, I would recommend reading up on some naming conventions so you don't mixed up between class names and variable names in the future.

iOS NSMutableArray Memory Leak

I'm having a bit of trouble with memory leaks in my objective c code. Could anyone take a look and let me know what they think?
NSStringArray.h
#interface NSStringArray : NSObject {
NSMutableArray *realArray;
}
#property (nonatomic, assign) NSMutableArray *realArray;
-(id)init;
-(void)dealloc;
#end
NSStringArray.m
#import "NSStringArray.h"
#implementation NSStringArray
#synthesize realArray;
-(id)init {
self = [super init];
if ( self != nil ) {
realArray = [[[NSMutableArray alloc] init] retain];
}
return self;
}
-(void)dealloc {
[realArray release];
realArray = nil;
[super dealloc];
}
Factory.m
+(NSStringArray *)getFields:(NSString *)line {
//Divides the lines into input fields using "," as the separator.
//Returns the separate fields from a given line. Strips out quotes & carriage returns.
line = [line stringByReplacingOccurrencesOfString:#"\"" withString:#""];
line = [line stringByReplacingOccurrencesOfString:#"\r" withString:#""];
NSStringArray *fields = [[NSStringArray alloc] init];
for (NSString *field in [line componentsSeparatedByString:#","]) {
[fields.realArray addObject:field];
[field release];
}
return [fields autorelease];
}
The Leaks tool is saying that the leak occurs when fields is allocated, and when I am adding field string to the fields array.
Also, this function is getting called each line of a file that I'm parsing.
Any tips would be helpful.
Thanks!
This line does a double retain:
realArray = [[[NSMutableArray alloc] init] retain];
it is enough
realArray = [[NSMutableArray alloc] init];
In this piece of code, you break the memory management rules.
for (NSString *field in [line componentsSeparatedByString:#","]) {
[fields.realArray addObject:field];
[field release];
}
You do not own the object pointed at by field so you must not release it.
You have overreleased field so the last object to release it (the autorelease pool in your case) is releasing an already dealloc'd object.
From the docs:
An allocation message does other important things besides allocating
memory:
It sets the object’s retain count to one (as described in “How Memory
Management Works”).
Therefore, you don't need to retain something that you've just alloc'ed.
Adding to Felz answer above. Use self.realArray when allocating array
self.realArray = [[NSMutableArray alloc] init];
Because you have created a property for the array so it is better to use "self"
You could also take advantage of the properties in objective C to make
more clear and efficient your code:
NSStringArray.h
#interface NSStringArray : NSObject {
}
#property (nonatomic, retain) NSMutableArray *realArray;
#end
NSStringArray.m
#import "NSStringArray.h"
#implementation NSStringArray
#synthesize realArray = _realArray;
-(id)init {
self = [super init];
if (self) {
self.realArray = [NSMutableArray array];
}
return self;
}
-(void)dealloc {
[_realArray release];
[super dealloc];
}
Now, with the modifier retain of the property realArray you can use
[NSMutableArray array] that return an autorelease mutable array.
The retain properties manage the retain/release stuff by themselves.
You don't need to use the realArray = nil; line. You've already deallocated
the property.
Hope this can help.

Using self.objectname causes profiler to report a memory leak

Please help;
Header File
#import <Foundation/Foundation.h>
#interface MyClass : NSObject {
NSMutableString * myString;
}
#property (nonatomic, retain) NSMutableString * myString;
-(id) init;
-(void) dealloc;
#end
Implementation File
#import "MyClass.h"
#implementation MyClass
#synthesize myString;
-(id) init {
if ((self = [super init])) {
self.myString = [[NSMutableString alloc] init];
}
return self;
}
-(void) dealloc {
[super dealloc];
[self.myString release];
}
#end
Usage
MyClass * m = [[MyClass alloc] init];
[m release];
//-- Xcode 4 profiler reports a memory leak here.
However, when the code in implementation file of the class is changed to not use the [self.myString .....] notation, then no memory leak is reported.
So,
-(id) init {
if ((self = [super init])) {
myString = [[NSMutableString alloc] init];
}
return self;
}
}
and
-(void) dealloc {
[super dealloc];
[myString release];
}
works fine. No memory leaks reported.
Any ideas - is it profiler or is it me (be nice)?
Your memory leak is not caused by using your setter. Your memory leak is caused by you not managing your memory correctly!
If you declare the following property
#property (nonatomic, retain) id value;
That means that the compiler generates methods that look something like this (highly simplified):
- (id)value {
return value;
}
- (void)setValue:(id)aValue {
[value autorelease];
value = [aValue retain];
}
When you use dot-notation, self.value = obj is desugared into [self setValue:obj]. Thence, you are actually causing obj to be retained within the setter. If you initially create an owning reference to obj (by using an +alloc without a corresponding -release or -autorelease), you'll have over-retained obj, and it will never be deallocated. Hence, you need to do something like this:
id obj = [[[NSObject alloc] init] autorelease];
self.value = obj;
or
id obj = [[NSObject alloc] init];
self.value = [obj autorelease];
or
id obj = [[NSObject alloc] init];
self.value = obj;
[obj release];
Whatever you do, you need to make sure that when you assert ownership of an object (by retaining it), you also release it.
Setter methods in Objective-C equate to a reatain of the new object and release of the old object. In your case the compiler will generate a setter method for your myString property that looks something like...
- (void)setMyString:(NSMutableString*)aString {
[myString autorelease];
myString = [aString retain];
}
When you invoke self.myString = in your init method this translates to a call to the setter. The setter in turn retains the object you pass to it. Because you've directly alloc'd the NSString it begins life with a retain count of one, you then call the setter and the retain count becomes two.
There's two approaches to fixing the problem, the first would be to add a call to [myString autorelease] after you alloc it. Or secondly switch your init method to directly assign the ivar...
// in your init method...
myString = [[NSMutableString alloc] init];
It's a good idea to avoid setter usage in init methods, not because of retain counts but because the object as a whole is not yet fully initialized.
#property (nonatomic, RETAIN)
you are retaining my friend. You have to release the object twice then because the retain count is 2
here is what you should do in the INIT method:
NSString *str = [[NSString alloc] initWithString:#"Hello World!"];
self.myString = str;
[str release]; // dont leak
Also I do not recommend using self.someProperty in the class itself. Doing so requires 1 extra objc_msgSend() to be done to access your variable and will slow down your application.

Objective-C memory management: do I have to release what I alloc?

I'm a newbie Objective-C developer (for iPhone development) and I have a question about memory management.
my code is just below;
Model1* model = [[Model1 alloc] init];
model.username = [[NSString alloc] initWithString:#"ysnky"];
[model.username release]; // is it needed
do I have to release model.username since I alloc?
Model1.h
#interface Model1 : NSObject {
NSString* username;
}
#property (nonatomic, retain) NSString* username;
#end
Model1.m
#implementation Model1
#synthesize username;
-(void) dealloc {
[username release];
[super dealloc];
}
#end
With the code as you have written it, yes, you have to release that string because as you've noticed it's a string you alloc/init'ed and then the property causes an additional retain.
But you could also just do:
model.username = #"ysnky";
First of all please use code tag to wrap your code.
do something like:
model.username = [[NSString alloc] initWithString:#"ysnky"];
is very discouraged you can use isntead:
model.username = #"ysnky"
in this case you don't have to worry about the object release.
But if you want to use
model.username = [[NSString alloc] initWithString:#"ysnky"];
the response to your question is NO. You don't need to call
[model.username release]
or to be more precisous your code is wrong, you have to do something like:
Model1* model = [[Model1 alloc] init];
NSString *tmpString = [[NSString alloc] initWithString:#"ysnky"]
model.username = tmpString;
[tmpString release];
finally I suggest to you to best design your class, you can make better by doing a initializer like:
Model1* model = [[Model1 alloc] initWithUsername:#"skdnaodnsa"];

Sending message to object that is member of array

Really hoping someone can help me sort out why sending objects that are members of an array seems to be the hardest thing in the world in Obj-C.
Here's the setup: I have a car class. Car has two member objects, engine and tire (of which there are four). Then I have an NSArray (also a member of car) initialized to hold the tire objects. I did this because I cannot figure out how to write or synthesize getter methods for just declaring like Tire *tires[4] (so I have to use NSArray and use objectAtIndex.
Here is the car class:
#import "Tire.h"
#import "Engine.h"
#interface Car : NSObject
{
Engine *engine;
Tire *tire1;
Tire *tire2;
Tire *tire3;
Tire *tire4;
NSArray *tirearray;
}
#property (nonatomic, copy) id engine;
#property (nonatomic, copy) id tire;
#property (nonatomic, copy) id tirearray;
#implementation Car
#synthesize engine;
#synthesize tire;
#synthesize tirearray;
- (void) print {
NSLog(#"%#", engine);
}
- (id) init {
if (self = [super init]) {
engine = [Engine new];
tire1 = [[tire alloc] init];
tire2 = [[tire alloc] init];
tire3 = [[tire alloc] init];
tire4 = [[tire alloc] init];
tirearray = [NSArray arrayWithObjects: tire1, tire2, tire3, tire4, nil];
}
return (self);
}
Then main:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Car *car = [[Car alloc] init];
[[car.tirearray objectAtIndex:0] setPressure: 32];
[pool drain];
return 0;
}
What I'm trying to do is figure out how to send messages to the objects in the array! That's all I want to do. The above code builds, but returns uncaught exception 'NSRangeException', reason: '*** -[NSArray objectAtIndex:]: index (0) beyond bounds (0)' !!!
Just so you know, pressure is just a member variable of the tire class, and the getter methods have been synthesized.
Then I want to print something to the console like "The pressure of tire X is X PSI".
This is driving me nuts! This should be simple! AHHHH.
Thanks in advance!
The code
tire1 = [[tire alloc] init];
should be
tire1 = [[Tire alloc] init];
Who told you to declare a property as an id? That's a very, very, very bad practice and you should stop it now. Right now.
If you bought a textbook which says so, please just burn it to ashes now.
By declaring your property
#property (nonatomic, copy) Tire* tire;
the compiler warn you at
tire1 = [[tire alloc] init];
saying that tire doesn't respond to alloc.
Oh man. I feel so silly. I didn't initialize the array at all! I need to alloc then initialize with initWithObjects. Haha. Oops.