Trying to use class variables in thread and getting EXC_BAS_ACCESS.
Code snippet:
#interface ViewController : UIViewController {
NSString* accountLoginName;
NSString* accountPassword;
}
in implementation:
accountLoginName = [NSString stringWithString:textFieldLoginName.text];
accountPassword = [NSString stringWithString:textFieldPassword.text];
[self performSelectorInBackground:#selector(loginAtBackgroundSelector:) withObject:nil];
-(void)loginAtBackgroundSelector:(UIAlertView*)alert
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"%#\n%#", accountLoginName, accountPassword);
[self login];
[self dismissAlert:alert];
[pool release];
}
just trying to write to console and getting error in this part of code, but error in the loginAtBackgroundSelector appears from time to time.
-(AlertType)login
{
NSLog(#"%#\n%#", accountLoginName, accountPassword);
}
Try this in interface:
#interface ViewController : UIViewController {
NSString* accountLoginName;
NSString* accountPassword;
}
#property(nonatomic, retain) NSString* accountLoginName;
#property(nonatomic, retain) NSString* accountPassword;
And this in the implementation (in the place where you assign the values):
self.accountLoginName = [NSString stringWithString:textFieldLoginName.text];
self.accountPassword = [NSString stringWithString:textFieldPassword.text];
[self performSelectorInBackground:#selector(loginAtBackgroundSelector:) withObject:nil];
Additionaly in the dealloc:
-(void)dealloc {
[accountLoginName release];
[accountPassword release];
[super dealloc];
}
Let me know if it helps.
Related
I'm trying to save the name of a button using a singleton so that the name can be accessed in another view to play a video with the same name. However, I'm getting the error: SIGABRT. I don't really see what's wrong with my code. Any ideas?
#import "List.h"
#import "MyManager.h"
#import "Video.h"
#implementation ExerciseList
-(IBAction) goToVideo:(UIButton *) sender{
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.vidName = [[sender titleLabel] text];
Video *videoGo = [[Video alloc] initWithNibName: #"Video" bundle: nil];
[self.navigationController pushViewController: videoGo animated: YES];
[videoGo release];
}
Here is my .h and .m for MyManager:
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSMutableArray *workouts;
NSString *vidName;
}
#property (nonatomic, retain) NSMutableArray *workouts;
#property (nonatomic, retain) NSString *vidName;
+ (id)sharedManager;
#end
#import "MyManager.h"
static MyManager *sharedMyManager = nil;
#implementation MyManager
#synthesize workouts;
#synthesize vidName;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if ((self = [super init])) {
workouts = [[NSMutableArray alloc] init];
vidName = [[NSString alloc] init];
}
return self;
}
-(void) dealloc{
self.workouts = nil;
self.vidName = nil;
[super dealloc];
}
#end
You should access the title of the button
sharedManger.vidName = [sender currentTitle];
However you are not using ARC so also check where your vidName property is retain or copy.
if it is not retain or copy then you can use this code also
if(sharedManger.vidname != nil){
[sharedManger.vidName release];
sharedManger.vidName = nil;
}
sharedManger.vidName = [[sender currentTitle] retain];
Im am using a pattern from the book Learning Cocos2D. I have a GameState class that is a singleton that can be saved as a state. The BOOL soundOn gets init as false the first time the class gest created, but the array levelProgress dosent. I made comments on all the lines on which I have tried to init the array.
The class uses a helper that loads and saves the data.
When i try 1 or 2 i get "instance variable 'levelProgress' accessed in class method" error.
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface GameState : NSObject <NSCoding> {
BOOL soundOn;
NSMutableArray *levelProgress;
}
+ (GameState *) sharedInstance;
- (void)save;
#property (assign) BOOL soundOn;
#property (nonatomic, retain) NSMutableArray *levelProgress;
#end
#import "GameState.h"
#import "GCDatabase.h"
#implementation GameState
#synthesize soundOn;
#synthesize levelProgress;
static GameState *sharedInstance = nil;
+(GameState*)sharedInstance {
#synchronized([GameState class])
{
if(!sharedInstance) {
sharedInstance = [loadData(#"GameState") retain];
if (!sharedInstance) {
[[self alloc] init];
// 1. levelProgress = [[NSMutableArray alloc] init];
}
}
return sharedInstance;
}
return nil;
}
+(id)alloc
{
#synchronized ([GameState class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a \
second instance of the GameState singleton");
sharedInstance = [super alloc];
// 2. levelProgress = [[NSMutableArray alloc] init];
return sharedInstance;
}
return nil;
}
- (void)save {
saveData(self, #"GameState");
}
- (void)encodeWithCoder:(NSCoder *)encoder {
// 3. levelProgress = [[NSMutableArray alloc] init];
[encoder encodeBool:currentChoosenCountry forKey:#"soundOn"];
[encoder encodeObject:levelProgress forKey:#"levelProgress"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
// 4. levelProgress = [[NSMutableArray alloc] init];
soundOn = [decoder decodeBoolForKey:#"soundOn"];
levelProgress = [decoder decodeObjectForKey:#"levelProgress"];
}
return self;
}
#end
Solution:
*I just added av init method...*
-(id)init {
self = [super init];
if (self != nil) {
levelProgress = [[NSMutableArray alloc] init];
}
return self;
}
Try this (this code may contain syntax or logic errors - I wrote it in notepad):
#interface GameState : NSObject <NSCoding>
#property (nonatomic, readwrite) BOOL soundOn;
#property (nonatomic, retain) NSMutableArray *levelProgress;
+ (GameState *)sharedState;
- (void)writeDataToCache;
#end
//
#implementation GameState
#synthesize soundOn, levelProgress;
#pragma mark - Singleton
static GameState *sharedState = nil;
+ (void)initialize {
static BOOL initialized = NO;
if (!initialized) {
initialized = YES;
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:#"%#/gameState", pathCache]]) {
NSData *encodedObject = [[NSData alloc] initWithContentsOfFile:[NSString stringWithFormat:#"%#/gameState", pathCache]];
data = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
}
else
data = [[GameState alloc] init];
}
}
+ (GameState *)sharedState {
return sharedState;
}
#pragma mark - Initialization
- (id)init {
if (self = [super init]) { // will be inited while application first run
soundOn = NO;
levelProgress = [[NSMutableArray alloc] init];
return self;
}
return nil;
}
#pragma mark - Coding Implementation
- (void)writeDataToCache { // use this method to save current state to cache
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:self];
if([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache]])
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache] error:nil];
[[NSFileManager defaultManager] createFileAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache] contents:encodedObject attributes:nil];
NSLog(#"GameState was saved successfully.");
}
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeBool:self.soundOn forKey:#"soundOn"];
[encoder encodeObject:self.levelProgress forKey:#"levelProgress"];
}
- (id)initWithCoder:(NSCoder*)decoder {
if ((self = [super init])) {
self.soundOn = [decoder decodeBoolForKey:#"soundOn"];
self.levelProgress = [decoder decodeObjectForKey:#"levelProgress"];
NSLog(#"GameState was inited successfully");
return self;
}
return nil;
}
#end
1 & 2 are the same (and should not work, since levelProgress is an instance variable), 3 and 4 should be encoding/decoding the array, not initializing it anew.
1 and 2 are a mistake - you can't access instance variable without specifying an instance.
You can fix 1 by changing the line to sharedInstance.levelProgress = ....
2 is just a bad idea, you're breaking common convention of initializing using init... methods, it may surprise and cause problems for an other programmer who'll be working on the same code later.
3 and 4 are ok but if loadData fails they will not be executed and the object will be initialized with ordinary init and the array will be nil pointer:
[[self alloc] init];
you should also override init and initialize the properties there.
I have spent hours on this and read everybit on the web about memory management, zombies. leaks (tried instruments). But I cannot figure this one out. Does someone has a clue? I am getting a EXC_BAD_ACCESS at the following like of code on popping the ChildViewController.
[profileVO release]; profileVO = nil;
I strongly feel I have followed all memory management best practices!
Details:
I have a model file. (CachedProfileVO)
CachedProfileVO.h
#interface CachedProfileVO : NSObject {
NSString *name;
NSString *email;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *email;
#end
CachedProfileVO.m
#synthesize name, email;
- (void) dealloc
{
[super dealloc];
[name release]; name= nil;
[email release]; email = nil;
}
Now I have a UINavigationController. The ParentViewController and the ChildViewController.
I invoke the ChildViewController as follows:
[self.navigationCntroller pushViewcontroller:childViewcontroller animated:YES];
In the ChildViewController, I basically use the model CachedProfileVO. However when this view controller is popped (back button on UI), it gives a EXC_BAD_ACCESS
ChildViewController.h
#interface ChildViewcontroller : UITableViewController {
CachedProfileVO *profileVO;
}
#property (nonatomic, retain) CachedProfileVO *profileVO;
#end
ChildViewController.m
#synthesize profileVO;
- (void) dealloc
{
[super dealloc];
[profileVO release]; profileVO = nil; ****** GETTING EXE_BAD_ACCESS here
}
- (void) viewDidLoad
{
CachedProfileVO *vo = [CachedProfileVO alloc] init];
self.profileVO = vo;
[vo release];
}
//responseString looks like this: [Murdoch, murdoch#email.com][other data][more data]
- (void) populateProfile:(NSString *) responseString
{
NSMutableString *str = [[NSMutableString alloc] initWithCapacity:20];
[str setString:responseString];
[str deleteCharactersInRange: NSMakeRange(0,1)];
[str deleteCharactersInRange: NSMakeRange([str length]-1,1)];
NSArray *tempArray = [str componentsSeparatedByString: #"]["];
NSString *tempStr = (NSString*)[tempArray objectAtIndex:0];
NSArray *bioArray = [tempStr componentsSeparatedByString:#","];
self.profileVO.name = (NSString*)[bioArray objectAtIndex:0];
self.profileVO.email= (NSString*)[bioArray objectAtIndex:1];
[str release]; str = nil;
}
Note that the function populateProfile is called after some event. I know it is called. And dealloc then causes the problem. Also this does not happen in every pop. I have to try several times to reproduce. It is never reproduced using zombies in instruments!!!
You are calling [super dealloc]; first in your examples. That should always be the last call otherwise you are accessing instance variables that belong to a now deallocated class. The following should work fine if you followed memory management rules elsewhere.
- (void) dealloc
{
[profileVO release];
[super dealloc];
}
Im using a threading class (.h/.m below) where the subclass is UIViewcontroller works without any issues.
#interface myFirstClass : UIViewController <MyOperationDelegate>{
However when I use it where the subclass is a NSobject to call a reachability class checking for internet connection, the App crashes when calling performSelectorOnMainThread? I dont understand why, there are no error when I build the App and when it crashes all i get is EXC_BAS_ACCESS. Is it not possible to do this when dealing with an NSObject? Any suggestion will be helpful for me.
#interface AppController : NSObject <MyOperationDelegate>{
myThreading.h
#protocol MyOperationDelegate
#required
-(void) updatedStatus:(NSArray*)items;
-(void) failedStatusWithError:(NSError*)error;
#end
#interface MyOperation : NSObject {
NSObject<MyOperationDelegate> * delegate;
NSOperationQueue *queue;
}
#property (retain) NSObject<MyOperationDelegate> *delegate;
-(void)load: (NSString *)stringUrlPath:(NSString *)functionAction;
#end
myThreading.m
#interface MyOperation (NSObject)
-(void)dispatchLoadingOperation:(NSDictionary *)aParameters;
#end
#implementation MyOperation
#synthesize delegate;
-(id)init
{
if ([super init]!=nil) {
queue = [NSOperationQueue new];
[queue setMaxConcurrentOperationCount:1];
}
return self;
}
-(void)load: (NSString *)stringUrlPath: (NSString *)functionAction {
[self dispatchLoadingOperation:[NSDictionary dictionaryWithObjectsAndKeys:
stringUrlPath, #"urlString", functionAction, #"action", nil]];
}
-(void)dealloc {
[queue cancelAllOperations];
self.delegate = nil;
[super dealloc];
}
-(void)dispatchLoadingOperation:(NSDictionary *)aParameters {
if([aParameters objectForKey:#"action"] == #"getStatus"){
#synchronized(self) {
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(fetchCheckStatus:)
object:aParameters];
[queue addOperation:operation];
[operation release];
}
}
}
-(void) fetchCheckStatus:(NSDictionary *)aParameters
{
NSData* data = [[NSMutableData alloc] initWithContentsOfURL:[NSURL URLWithString:[aParameters objectForKey:#"urlString"]] ];
NSError *error;
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (responseString != nil) {
NSMutableArray *rssItems;
[self.delegate performSelectorOnMainThread:#selector(updatedStatus:) withObject:[NSArray arrayWithObjects:rssItems, nil] waitUntilDone:NO];
} else {
[queue cancelAllOperations];
[self.delegate performSelectorOnMainThread:#selector(failedStatusWithError:) withObject:error waitUntilDone:NO];
}
[responseString autorelease];
[data release];
}
#end
The problem are these lines:
NSMutableArray *rssItems;
[self.delegate performSelectorOnMainThread:#selector(updatedStatus:) withObject:[NSArray arrayWithObjects:rssItems, nil] waitUntilDone:NO];
You declare a variable rssItems but don't set it. It will contain random garbage from the stack which will then be interpreted as a pointer. Maybe sometimes you're lucky and the value is actually a pointer to a living object, but more likely dereferencing it causes your crash.
You need to actually initialize the variable, e.g.:
NSMutableArray *rssItems = nil;
but I guess you really want:
NSMutableArray *rssItems = [NSMutableArray array];
I've got a leak in my application and I do not know why. Maybe I've got all memory managment thing wrong. In my code I've got UIViewController object which have ivar TelephoneValidator *validator
TelephoneValidator is TelephoneValidator : NSObject
So in my initialization function of UIViewController object (initWithFieldData) I've got:
-(id) initWithFieldData: (NSMutableDictionary*) fieldData
{
...
validatorOptions = [fieldData objectForKey:#"fieldValidator"];
...
}
Now in my viewDidLoad I've got:
- (void)viewDidLoad {
...
if (![validatorOptions respondsToSelector:#selector(isEqualToString:)]) {
validator = [[TelephoneValidator alloc] initWithOptions: validatorOptions];
}
else {
validator = nil;
}
...
}
Basicly if my validatorOptions isn't NSString the validator ivar became TelephoneValidator instance.
In my dealloc:
- (void)dealloc {
if(validator != nil)
{
[validator release];
validator = nil;
}
...
[super dealloc];
}
I've checked a couple of times if dealloc works, and it is. After calling dealloc the validator is released (calling any method on validator after [validator release] gets me exception).
And yet in Instruments it is telling me that TelephoneValidator is leaked. And after double clicking in Instruments the line of code that is highlited is:
validator = [[TelephoneValidator alloc] initWithOptions: validatorOptions];
What am I doing wrong?
UPDATE:
Here is my header information of UIViewController:
#interface GenericViewController : UIViewController <UITextFieldDelegate>{
UIImage *backgroundImage;
NSString *step; // na ktorym kroku jestesmy
id <GenericControllerDelegate> delegate; //delegata z ktorej bedziemy pobierali dane
UITextField *textField;
NSString *fieldName; //nazwa pola (potrzebujemy zeby zapisac do modelu odpowiedni tekst
UILabel *textLabel;
UILabel *stepsLabel;
UILabel *prefixTextLabel;
NSString *fieldPlaceholder;
NSString *textLabelText;
NSString *textLabelTextPl; //w jezyku polskim
NSString *prefixTextLabelText; //w jezyku eng
NSString *prefixTextLabelTextPl; //w jezyku polskim prefix
NSString *fieldRequired;
NSString *keyboardType;
NSString *capitalizeType;
UIButton *button; //forward button
UIButton *button2; //backward button
//to bedzie do przerobienia bo bedziemy mieli tablicje walidatorow a nie jeden walidator
NSString *validatorType;
//maksymalna dlugosc pola
int maxLengthOfTextField;
NSArray* validatorOptions;
TelephoneValidator *validator;
//patientModel
PatientData *patientModel;
}
TelephoneValidator header:
#import <Foundation/Foundation.h>
#import "MAOTranslate.h"
#interface TelephoneValidator : NSObject {
//opcje walidacyjne
NSString *phonePrefix;
NSString *phonePostfix;
int phoneLength;
NSString *message;
NSString *messagePl;
UIAlertView *alertView;
}
-(id) initWithOptions:(NSArray *) optionsArray;
-(void) displayMessage;
-(BOOL) validate: (NSString *) phoneNumber;
#end
TelephoneValidator class:
#import "TelephoneValidator.h"
#implementation TelephoneValidator
//#synthesize phoneNumber;
-(id) initWithOptions:(NSArray *) optionsArray;
{
if(self = [[TelephoneValidator alloc] init])
{
phonePrefix = [optionsArray objectAtIndex:0];
phonePostfix = [optionsArray objectAtIndex:1];
phoneLength = [[optionsArray objectAtIndex:2] intValue];
message = [optionsArray objectAtIndex:3];
messagePl = [optionsArray objectAtIndex:4];
}
else {
self = nil;
}
return self;
}
//wyswietlamy wiadomosc
-(void) displayMessage
{
NSString *displayMsg;
if ([[MAOTranslate getLanguage] isEqualToString:#"pl"]) {
displayMsg = messagePl;
}
else {
displayMsg = message;
}
alertView = [[UIAlertView alloc] initWithTitle:#"Alert" message:displayMsg delegate:self cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alertView show];
}
-(BOOL) validate: (NSString *) phoneNumber
{
//dlugosc
if ([phoneNumber length] != phoneLength) {
NSLog(#"zla dlugosc");
return NO;
}
NSLog(#"tutaj");
//sprawdzamy prefix
if ([phonePrefix length]!= 0) {
NSLog(#"w srodku ifa");
if ([phoneNumber compare:phonePrefix options:NSLiteralSearch range:NSMakeRange(0, [phonePrefix length])] != 0) {
NSLog(#"zly prefix");
[self displayMessage];
return NO;
}
}
//sprawdzamy postfix
if([phonePostfix length] != 0)
{
if ([phoneNumber compare:phonePostfix options:NSLiteralSearch range:NSMakeRange([phoneNumber length]-[phonePostfix length], [phonePostfix length])] != 0) {
NSLog(#"zly postfix");
[self displayMessage];
return NO;
}
}
//sprawdzamy czy string jest numeryczny
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:phoneNumber];
if (![alphaNums isSupersetOfSet:inStringSet])
{
NSLog(#"zly format ");
[self displayMessage];
return NO;
}
return YES; //zwalidowany poprawnie
}
-(void) dealloc
{
[alertView release];
alertView = nil;
[super dealloc];
}
You need to call [super dealloc] at the end of the dealloc method.
See These both lines
validator = [[TelephoneValidator alloc] initWithOptions: validatorOptions];
and inside initWithOptions
if(self = [[TelephoneValidator alloc] init])
You are allocing twice the validator, so there is a leak.
Could it be that instruments is pointing to validatorOptions as the source of the leak? Is it a retained property being released at dealloc or not? I can't say for sure, the code you posted is not enough to arrive to a conclusion.
Also, as willcodejavaforfood says, you must always call [super dealloc]; at the end of your dealloc method. No code must come after it.
Edit:
I'm back. But Bruno Domingues got it right already, you are allocating twice, in which case, the first one leaks. You should change your -initWithOptions: code to:
-(id) initWithOptions:(NSArray *) optionsArray;
{
if((self = [super init])){
// ... rest of code is fine
}
return self;
}