Exc_Bad_Access on main function and unable to pass instance variables through classes - objective-c

Updated post
Now, I have a EXC_BAD_ACCESS on the main 2 times out of 7 and I don't know why the heightOfPumpView result is 0 from the pumpCustomView class when the result of pumpViewHeight is 607.
PumpViewController.m
#import "PumpViewController.h"
#import "PumpModel.h"
#import "PumpCustomView.h"
#implementation PumpViewController
#synthesize labels;
#synthesize heightOfPumpView;
- (id)init
{
if (self = [super init])
{
labels = [[PumpModel alloc]init];
PumpCustomView* pumpView = [PumpCustomView alloc];
heightOfPumpView = [pumpView pumpViewHeight];
[labels pumpCreateLabel:heightOfPumpView];
labelsArray = [[NSMutableArray alloc]initWithArray:[labels labelsGroup]];
[labels release];
if (labelsArray!=nil)
{
[pumpView addSubview:[labelsArray objectAtIndex:2]];
}
[labelsArray release];
[pumpView release];
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
#end
PumpModel.m
#import "PumpModel.h"
#import "PumpViewController.h"
#import "PumpCustomView.h"
#implementation PumpModel
#synthesize labelsGroup;
-(id)init
{
self = [super init];
return self;
}
-(void)pumpCreateLabel:(float)pumpViewHeight
{
theNumberOfPump = 8;
PumpViewController* pumpViewControllerAlloc = [PumpViewController alloc];
labelsGroup = [[NSMutableArray alloc]init];
for (int i = 0;i < theNumberOfPump; i++)
{
int pumpViewHeight = [pumpViewControllerAlloc heightOfPumpView];
int pumpViewWidthA = 259;
int resultHeight = pumpViewHeight/theNumberOfPump;
CGFloat resultWidth = pumpViewWidthA/2;
positionChart[i] = resultHeight * i;
newLabel[i] = [[NSTextField alloc] init] ;
[newLabel[i] setIntValue:i];
newLabel[i].frame = CGRectMake(resultWidth, positionChart[i], 300, 100);
newLabel[i].font= [NSFont fontWithName:#"Arial" size:12];
newLabel[i].textColor= [NSColor blackColor];
newLabel[i].backgroundColor= [NSColor whiteColor];
[labelsGroup addObject:newLabel[i]];
[newLabel[i] release];
NSLog(#"%# %d",[[labelsGroup objectAtIndex:i] stringValue],positionChart[i]);
}
[pumpViewControllerAlloc release];
}
-(void) dealloc
{
[labelsGroup release];
[super dealloc];
}

You shouldn't send messages to the object before [super init], e.g.:
- (id)init
{
if (self = [super init])
{
[self setNumberOfPump:8];
}
return self;
}
This is also true for:
-(id)initWithNumberOfPump:(int)numberOfPump
{
if (self = [super init]) {
theNumberOfPump = numberOfPump;
[self pumpCreateLabel];
}
return self ;
}

If you have a crash, post the backtrace of the crash.
Looking at your setNumberOfPump: method, it seems quite wrong.
labels is allocated, then released, likely leaving the instance variable as a dangling reference that'll crash later
labelsArray is leaked
your dealloc doesn't release any memory
You should try running Build and Analyze on your code, fixing any errors. The above issues combined with comments regarding the init patterns indicates that you should likely review the Objective-C documentation to gain a better understanding of both initialization and memory management patterns.

Related

Objective C - NSMutableSet not working properly

I have an NSMutableSet that I am trying to add labels to. After each label added, I check the set's count and it comes back as 0. Any help will be appreciated
In my .h file:
#interface MainMenu : CCLayerColor {
NSMutableSet* letters;
}
In my .m file:
-(void)initiateLetters{
//Grab the window size
CGSize size = [[CCDirector sharedDirector] winSize];
int i;
for(i=0;i<100;i++){
CCLabelTTF *label;
int r = rand() % 35 + 60;
char c = (char) r;
label = [CCLabelTTF labelWithString:[NSString stringWithFormat:#"%c",c] fontName:#"Courier" fontSize:30];
[label setColor:ccc3(0,0,0)];
[label setOpacity:255/2];
//Generate a random number for the x variable, y will be 0
int x = (arc4random() % (int)size.width);
int y = size.height+(arc4random() % 50)+25;
[label setPosition:ccp(x,y)];
[self addChild:label];
[letters addObject:label];
//Here's what's printing 0:
printf("%lu",[letters count]);
}
}
You have to instantiate the set before you can add things to it. You can do that in an overridden implementation of init:
- (id)init
{
self = [super init];
if (self) {
letters = [[NSMutableSet alloc] init];
}
return self;
}
…or at the beginning of your initiateLetters method:
- (void)initiateLetters
{
letters = [[NSMutableSet alloc] init];
...
Instead, the code you posted is simply sending addObject: to nil, which does nothing.
the array is probably nil. Did you create it in the class init method ?
something like
-(id) init {
if (self = [super init]){
// ...
letters = [[NSMutableSet set] retain]; // not using ARC
// ...
}
return self;
}
and of course
-(void) dealloc {
// ...
[letters release]; // not using ARC
[super dealloc];
}

Memory leak in [[NSMutableArray alloc] init] even though with ARC

I am working heavily on Memory Fineturning and most of the problems are solved with the help of stackoverflow. But finally I got struck with a serious memory leak with NSMutableArray initialization.
- (NSMutableArray *)children {
if (!_children) {
_children = [NSMutableArray new]; // <-- here is the memory leak
}
return _children;
}
I am working with a project where we are using 100% ARC.
Perticular class initialization is not triggered none other than the main thread (no threading has been used).
but... this is the only place where we are using recurtion
moreover the statments
#property (strong, nonatomic) NSMutableArray *children;
#synthesize children = _children;
are there in the respective .h and .m files...
Waht can be the problem....?
Thanks in advance for you effort...
more info...
- (id)init {
if (self = [super init]) {
[self setIsRoot:NO];
// [self setChildren:[NSMutableArray new]]; // <-- was before, but I moved to custom setter
}
return self;
}
- (id)initAsRoot
{
if (self = [self init]) {
self.level = -1;
self.index = -1;
self.isExpaned = YES;
self.value = #"Root";
self.isRoot = YES;
}
return self;
}

Can I initiate an ivar indirectly?

I'm trying to initiate my ivar like this:
Declared like this in h-file
#interface MyClass: {
UITextView *_myTextView;
}
then created like this in the m-file
- (id)init {
self = [super init];
if(self) {
[self initTextView:_myTextView];
}
return self;
}
- (void)initTextView:(UITextView *)textView {
textView = [[UITextView alloc] init];
...
}
_myTextView will still be nil afterwards. Why is that and what should I do it to make it work? I've got ARC enabled.
[EDIT]
This works. Thanks all!
- (id)init {
self = [super init];
if (self) {
_textView1 = [self createTextView];
_textView2 = [self createTextView];
_textView3 = [self createTextView];
}
return self;
}
- (UITextView *)createTextView {
UITextView *textView = [[UITextView alloc] init];
...
return textView;
}
You need to always refer to instance variables using:
self.textView = [[UITextView alloc] init];
Also use a name other than initTextView as methods starting with init have special meaning in Objective-C.
If you want to use the same code to initialize multiple text view controls, then use code like this:
- (UITextView *)createTextView
{
UITextView *textView = [[UITextView alloc] init];
textView.something = whatever;
...
return textView;
}
And then use it like this:
- (id)init {
self = [super init];
if(self)
{
self.textView1 = [self createTextView];
self.textView2 = [self createTextView];
...
self.textViewN = [self createTextView];
}
}
In [self initTextView:_myTextView]; you pass the current value of _myTextView (which is nil) to your initTextView: method. To set the instance variable, you need a pointer to a pointer.
- (id)init {
self = [super init];
if (self) {
[self setupTextView:&_myTextView];
}
return self;
}
- (void)setupTextView:(UITextView * __strong *)textView {
*textView = [[UITextView alloc] init];
...
}
I also renamed the initTextView: method to setupTextView, as methods starting with init are expected to behave like other init methods in ARC.
- (id)init {
self = [super init];
if(self) {
[self initTextView];
}
}
- (void)initTextView{
_myTextView = [[UITextView alloc] init];
...
}
if you want to call initTextView for several text views , you can code like this :
- (id)init {
self = [super init];
if(self) {
_myTextView = [[UITextView alloc] init];
[self initTextView:_myTextView];
}
}
- (void)initTextView:(UITextView *)textView{
//setup the textView
...
}

NSCondition ->Objective C

I am a newbie to Objective-C. I'm currently working on threads.
I have to make a synchronous execution of threads. I'm using NSInvocationOperaion to spawn a thread.
I have two threads. I need to wait for the 1st thread to signal a event or the timeout.
Signalling a event can be done by NSConditionLock. How to signal a timeout. I could not use waitUntilDate method here as the timeout is not a fixed value.
Is there any way to do this?
EDITED
main.m
------
#import "PseudoSerialQueue.h"
#import "PseudoTask.h"
int main()
{
PseudoSerialQueue* q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test2)];
[q quit];
return 0;
}
PseudoTask.h
-----------------
#import <Foundation/Foundation.h>
#interface PseudoTask : NSObject {
id target_;
SEL selector_;
id queue_;
}
#property(nonatomic,readonly)id target;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
-(void)exec;
#end
PseudoTask.m
-----------------
#import "PseudoTask.h"
#implementation PseudoTask
#synthesize target = target_;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue
{
self = [super init];
if (self) {
target_ = [target retain];
selector_ = selector;
queue_ = [queue retain];
}
return self;
}
-(void)exec
{
[target_ performSelector:selector_];
}
-(void)dealloc
{
[super dealloc];
[target_ release];
[queue_ release];
}
#end
PseudoSerialQueue.h
----------------------------
#import <Foundation/Foundation.h>
#import "PseudoTask.h"
#interface PseudoSerialQueue : NSObject {
NSCondition* condition_;
NSMutableArray* array_;
NSThread* thread_;
}
-(void)addTask:(id)target selector:(SEL)selector;
#end
PseudoSerialQueue.m
----------------------------
#import "PseudoSerialQueue.h"
#implementation PseudoSerialQueue
-(id)init
{
self = [super init];
if (self) {
array_ = [[NSMutableArray alloc]init];
condition_ = [[NSCondition alloc]init];
thread_ = [[NSThread alloc] initWithTarget:self selector:#selector(execQueue) object:nil];
[thread_ start];
}
return self;
}
-(void)addTask:(id)target selector:(SEL)selector
{
[condition_ lock];
PseudoTask* task = [[PseudoTask alloc] initWithTarget:target selector:selector queue:self];
[array_ addObject:task];
[condition_ signal];
[condition_ unlock];
}
-(void)quit
{
[self addTask:nil selector:nil];
}
-(void)execQueue
{
for(;;)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
[condition_ lock];
if (array_.count == 0) {
[condition_ wait];
}
PseudoTask* task = [array_ objectAtIndex:0];
[array_ removeObjectAtIndex:0];
[condition_ unlock];
if (!task.target) {
[pool drain];
break;
}
[task exec];
[task release];
[pool drain];
}
}
-(void)dealloc
{
[array_ release];
[condition_ release];
[super dealloc];
}
#end
I could not pass self from main.Hope i'm mistakenly calling it.
Error:'self' undeclared is coming.
I could not understand
-(void)exec
{
[target_ performSelector:selector_];
}
in PseudoTask.m
target_ is not a method and its an ivar.
I am not getting any error or warning.But i could not understand that code.
I am writing what i have understood from your program.Please correct me if i my way of understanding the program is wrong.
The Thread execQueue is spawned when the PseudoSerialQueue is initialised and it waits for the signal from the addTask method.
The addTask method is called in the quit method and the parameters passed are nil.I could not understand why to pass a nil parameter.
It would be helpful if you explain about it.Thanks.
You mean NSCondition? You can use waitUntilDate: as relative time.
[condition lock];
// wait 5 seconds.
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
[condition unlock];
EDITED:
My PseudoSerialQueue class requires to be called from a class that is derived from NSObject like the following.
#interface Test : NSObject
#end
#implementation Test
- (void)test0
{
}
- (void)test1
{
}
- (id)init
{
self = [super init];
return self;
}
- (void)exec
{
PseudoSerialQueue *q = [[PseudoSerialQueue alloc] init];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test0)];
[q quit];
}
#end
You can call it from main function.
Test *test = [[Test alloc] init];
[test exec];
I could not understand why to pass a nil parameter.
I just only chose it for the message of quitting the loop in the PseudoSerialQueue.
Let the 1st thread signal the 2nd one in both cases; then in the second thread you can tell in which case you are based on some read-only flag in the 1st controller or in your model (say, isDataAvailable).

Adding a custom initWith?

If I create a custom initWith for an object do I essentially include the code I would add should I want to override init?
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
}
return(self);
}
e.g.
-(id) initWithX:(int) inPosX andY:(int) inPosY {
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return(self);
}
gary
You can create one designated initializer that accepts all parameters that you want to make available in initialization.
Then you call from your other -(id)init your designated initializer with proper parameters.
Only the designated initializer will initialize super class [super init].
Example:
- (id)init
{
return [self initWithX:defaultX andY:defaultY];
}
- (id)initWithPosition:(NSPoint)position
{
return [self initWithX:position.x andY:position.y];
}
- (id)initWithX:(int)inPosX andY:(int)inPosY
{
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return self;
}
The designated initializer is -(id)initWithX:andY: and you call it from other initializers.
In case you want to extend this class you call your designated initializer from subclass.
I'd suggest creating one main initializer that handles most of the work. You can then create any number of other initializers that all call this main one. The advantage of this is if you want to change the initialization process, you'll only have to change one spot. It might look like this:
-(id) initWithX:(float)x {
if (self = [super init]) {
/* do most of initialization */
self.xVal = x;
}
return self;
}
-(id) init {
return [self initWithX:0.0f];
}
In this example initWithX: is our main initializer. The other initializer (init) simply calls initWithX: with a default value (in this case 0).
Yes, that's exactly how I do it. One slight change will cut out a line of code:
if (self = [super init]) {
As opposed to:
self = [super init];
if(self) {
For modern Objective-C ...
UDFile.h
#import <Foundation/Foundation.h>
#interface UDFile : NSObject
#property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
#end
UDFile.m
#import "UDFile.h"
#implementation UDFile
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = [name copy];
}
return self;
}
- (instancetype)init {
return [self initWithPathname:#""];
}
Sometimes, you want to reuse some initialisation code and modify the behaviour only slightly for specific initialisers. In this case, I do the following:
- (id) init
{
self = [super init];
if (!self) return nil;
// These values are always initialised this way
ivar1 = 10;
ivar2 = #"HellO";
ivar3 = [[NSMutableArray alloc] initWithCapacity:10];
ivar4 = 22;
return self;
}
- (id) initWithIvar4:(int) aValue
{
// call -init on self, which will call -init on super for us, and set
// up ivar1, ivar2, ivar3, and ivar4.
self = [self init];
if (!self) return nil;
// Change ivar4 from the default 22 to whatever aValue is.
ivar4 = aValue;
return self;
}