exc_bad_access from nsstring in an instance - HARD, BUT COOL - iphone

I have read the posts I found here and in many other places - no answers.
I have a simple class that contains NSString:
MyItem.h
#import <Foundation/Foundation.h>
#interface MyItem : NSObject
{
NSString * ItemName;
NSNumber * TestID;
NSMutableArray * Items;
}
//property
#property (nonatomic,retain) NSString * ItemName;
#property (nonatomic,retain) NSNumber * TestID;
#property (nonatomic,retain) NSMutableArray * Items;
// methods
-(id)initWithName:(NSString*)theName;
#end
MyItem.M
#import "MyItem.h"
#implementation MyItem
#synthesize Items;
#synthesize TestID;
#synthesize ItemName;
-(id)initWithName:(NSString*)theName
{
ItemName=theName;
Items=[[NSMutableArray alloc]init];
return self;
}
#end
It is very simple, as the class is created, the name is retained and the array allocated.
In order to have view controllers sharing this class, I have created this protocol:
MasterPager.h
#import <Foundation/Foundation.h>
#class MyItem;
#protocol MasterPager <NSObject>
#required
#property (nonatomic,retain) MyItem * currentItem;
-(void)dumpItems;
#end
which I then use in my appdelegate:
ArrayTestAppDelegate.h
#import <UIKit/UIKit.h>
#import "MasterPager.h"
#class ArrayTestViewController;
#interface ArrayTestAppDelegate : NSObject <UIApplicationDelegate,MasterPager>
{
//MyItem * currentItem;
}
#property (nonatomic,retain) MyItem * currentItem;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet ArrayTestViewController *viewController;
#end
I'm instanciating this property in the application didFinishLaunchingWithOptions as so:
#synthesize currentItem;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// Override point for customization after application launch.
currentItem=[[MyItem alloc] initWithName:#"main stuff"];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
self.viewController.mainPage=self;
return YES;
}
and here is the dumpItem method:
-(void)dumpItems
{
NSLog(#"Dumping %#",currentItem.ItemName);
for(int i=[currentItem.Items count]-1;i>=0;i--)
{
MyItem * item=[currentItem.Items objectAtIndex:i];
NSLog(#"current item id:%#", item.TestID );
NSLog(#"current item name:%#", item.ItemName );
}
}
(Sorry for all this text, but it is probably required).
Now, I have a view controller that I use in order to test this.
This view controller has 2 buttons, each of them triggers different function.
the first function to create some (4) sub items in this object is working fine:
-(IBAction)onCreate:(id)sender
{
for(int i=0;i<4;i++)
{
MyItem * item=[[MyItem alloc] initWithName :[NSString stringWithFormat:#"Test number %d",i]];
item.TestID=[NSNumber numberWithInt:i];
[mainPage.currentItem.Items addObject:item];
}
[mainPage dumpItems];
}
As you can see the dumpItems is called and it does what its suppose to do, dumping the objects.
********NOW... here is the thing!*************
There is a second button, as mentioned, that execute the same function:
- (IBAction)onDump:(id)sender
{
NSLog(#"executing dump on the protocol");
[mainPage dumpItems];
}
After creation, clicking the second button is calling this method which in turn calls the same dumpItems! BUT, when this is executed, an exc_bad_access is thrown when the line
NSLog(#"current item name:%#", item.ItemName );
is reached. comment the line and it's all working.
un-commenting the //MyItem * currentItem; will do nothing. So, how could it be?
NSZombieEnabled ? Tried that, did nothing.
There is no release call in sight, and if there were, how come the NSNumber dump working just fine?
Also, nothing happen between the first button clicked and the second one.
but still, the strings somehow disappears!

is this ARC? If not, it's not that hard, and not that cool ;-)
You pass an autoreleased NSString to your init method
MyItem * item=[[MyItem alloc] initWithName :[NSString stringWithFormat:#"Test number %d",i]];
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ autoreleased object
Unfortunately you don't retain that autoreleased string in your init.
-(id)initWithName:(NSString*)theName {
ItemName=theName; // <- no retain
}
the code steps out of the init method and you run dumpItems on the newly created object
[mainPage dumpItems]; // <- within same runloop iteration. AutoreleasePool has not be drained.
since you call dumpItems before the end of the current runloop the autoreleased object still exists.
But the IBAction method happens after the autoreleased object has been deallocated (the object was deallocated when the autorelease pool was drained at the end of the current runloop).
- (IBAction)onDump:(id)sender
{
NSLog(#"executing dump on the protocol");
[mainPage dumpItems]; // <- not in the same runloop. AutoreleasePool has been drained. Autoreleased object has been deallocated
}
the fix:
-(id)initWithName:(NSString *)theName
{
if ((self = [super init])) {
itemName = [theName retain]; // to match your #property
items = [[NSMutableArray alloc] init];
}
return self;
}
By the objective-c code style guidelines only Class names (e.g. NSString, MyItem) should start with a capital letter. You should fix this to improve readability (and the code formatting on stackoverflow)

I had the same problem, you should change the code from:
#property (nonatomic,retain) NSString * ItemName;
#property (nonatomic,retain) NSNumber * TestID;
#property (nonatomic,retain) NSMutableArray * Items;
to:
#property (nonatomic,copy) NSString * ItemName;
#property (nonatomic,copy) NSNumber * TestID;
#property (nonatomic,copy) NSMutableArray * Items;

Related

Making an Integer Array in Objective-C

I want to have an internal int array for my class, but I can't seem to get XCode to let me. The array size needs to be set on initialization so I can't put the size directly into the interface.
At the moment I've been trying:
#interface TestClass : NSObject {
int test[];
}
But it tells me that I'm not allowed. How to I refer to it in my interface, and then how do I allocate it when I create the implementation?
Sorry for a somewhat standard sounding question, but I can't seem to find the answer I need from searching.
edit: I want to use an array because it's apparently much faster than using an NSArray
You can use a number of methods to overcome this problem, but the easiest is to simply make the instance variable a pointer, like this:
#interface TestClass : NSObject {
int *test;
}
#property int *test;
#end
Synthesizing the property will give it getter and setter methods which you can use to set its contents:
#implementation TestClass
#synthesize test;
//contents of class
#end
You can then use it like this:
TestClass *pointerTest = [[TestClass alloc] init];
int *array = (int *)malloc(sizeof(int) * count);
//set values
[pointerTest setTest:array];
[pointerTest doSomething];
However, using objects like NSNumber in an NSArray is a better way to go, perhaps you could do something like this:
#interface TestClass : NSObject {
NSArray *objectArray;
}
#property (nonatomic, strong) NSArray *objectArray;
#end
#implementation TestClass
#synthesize objectArray;
//contents of class
#end
You can then set its contents with a pointer to an NSArray object:
NSArray *items = [NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2], nil];
TestClass *arrayClass = [[TestClass alloc] init];
[arrayClass setItems:items];
[arrayClass doSomething];
When retaining objects upon setting them (like the previous example), always make sure you deallocate the object in the classes dealloc method.
A C array is just a sufficiently sized raw memory buffer. Foundation has a nice wrapper around raw memory that frees you from all the manual memory management: NSMutableData
The following approach gives you automatic memory management plus proper encapsulation.
#interface TestClass : NSObject
#property (nonatomic, readonly) int *testArray;
#property (nonatomic, readonly) NSUInteger testArraySize;
#end
#implementation TestClass
{
NSMutableData *_testData;
}
- (id)initWithSize:(NSUInteger)size
{
self = [self init];
if (self != nil) {
_testData = [NSMutableData dataWithLength:size];
}
}
- (int *)testArray
{
return [_testData mutableBytes];
}
- (NSUInteger)testArraySize
{
return [_testData length];
}
#end
As you see, the ivar does not have to be declared in the #interface.
Try something like this:
#interface TestClass : NSObject
{
int *_test;
}
#property (assign) int *test;
#end
#implementation TestClass
- (instancetype)init
{
if (self = [super init])
{
_test = malloc(sizeof(int) * 20);
}
return self;
}
- (int *)test
{
return _test;
}
- (void)setTest:(int*)test
{
memcpy(&_test, &test, sizeof(_test));
}
- (void)dealloc
{
free(_test);
}
#end

Change text in UILabel with NSMutablearray data

I'm trying to change the text of a UILabel with text from an array upon a button click, but it doesn't do anything.
#interface Test01AppDelegate : NSObject <UIApplicationDelegate> {
UILabel *helloLabel;
UIButton *hellobutton;
NSMutableArray *madWords;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UIButton *hellowButton;
#property (nonatomic, retain) IBOutlet UILabel *hellowLabel;
#property (nonatomic, retain) NSMutableArray *madWords;
- (void) madArrays;
- (IBAction)helloYall;
#end
and
#import "Test01AppDelegate.h"
#implementation Test01AppDelegate
#synthesize window = _window;
#synthesize hellowButton;
#synthesize hellowLabel;
#synthesize madWords;
- (void) madArrays {
[madWords addObject:#"Part A"];
[madWords addObject:#"Part B"];
[madWords addObject:#"Part C"];
[madWords addObject:#"Part D"];
}
- (IBAction)helloYall {
[self madArrays];
self.hellowLabel.text = [madWords objectAtIndex:0];
}
I can set the helloLabel text with
#"some text here";
and it works fine. Also, I tried copying the "madArrays" method into the "helloYall" method and it still didn't work. As I said, I can manually set the text and it works, but I'd like to pull the info from an array. Eventually, I'd like to loop through the array to grab the text on each button press, but one step at a time. Thanks.
You never create the madWords array. You need to add:
self.madWords = [NSMutableArray array];
at the top of:
- (void) madArrays {
would probably be a good place. Other possibly good places would be i the class init method or the view controller viewWillAppear method.
// Or you can try this in your init Method:
//first allocate the ivar
- (void)myInitMethod {
madArrays = [[NSMutableArray]alloc]init];
}
//then you can do anything directly to the ivar or throughout de setter
- (void)doAnythingWithiVar {
// do your stuff
}
//when you are done you can dealloc your ivar
- (void)dealloc {
[madArrays release];
[super dealloc];
}
It looks like madArrays is still nil when you come to populate it. At some point you need something like [self setMadArrays:[[NSMutableArray alloc] init]];. Also, don't forget to release madArrays in the dealloc before calling super as you'll have a memory leak.

Using AppDelegate for global readonly data

I have a project on iPhone with iOS4.
A instance variable of app delegate is a dictionary with global readonly data loaded from a plist when app starts.
CalculatorAppDelegate.h
#import <UIKit/UIKit.h>
#class MainViewController;
#interface CalculatorAppDelegate : NSObject <UIApplicationDelegate> {
NSDictionary *RGBSpacesDictionary;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain, readonly) NSDictionary *RGBSpacesDictionary;
#property (nonatomic, retain) IBOutlet MainViewController *mainViewController;
#end
CalculatorAppDelegate.m
#import "CalculatorAppDelegate.h"
#import "MainViewController.h"
#implementation CalculatorAppDelegate
#synthesize mainViewController=_mainViewController;
#synthesize RGBSpacesDictionary;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// load plist
NSString* plistPath1 = [[NSBundle mainBundle] pathForResource:#"RGBSpaces" ofType:#"plist"];
RGBSpacesDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath1];
etc.
}
Then in MainViewController i am able to successfully reading the dictionary in viewDidLoad
MainViewController.h
#class CalculatorAppDelegate;
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate> {
CalculatorAppDelegate *appDelegate;
}
#property (nonatomic, retain) CalculatorAppDelegate *appDelegate;
etc.
}
MainViewCOntroller.m
#import "CalculatorAppDelegate.h"
#implementation MainViewController
#synthesize appDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
appDelegate = [[UIApplication sharedApplication] delegate];
RGBSpacesCount = (int) [appDelegate.RGBSpacesDictionary count];
}
In viewDidLoad it is all OK, I can read my dictionary as appDelegate.REGSpaceDictionary.
The problem is with another method of MainVievController called when a button is pressed
- (IBAction) RGBSpaceButtonPressed {
NSLog(#"appDelegate.RGBSpacesDictionary %#", appDelegate.RGBSpacesDictionary);
etc.
}
At this time calling the dictionary (for example with a NSLog) return in a crash.
Can someone help me? Thank you.
In this line
RGBSpacesDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath1];
you are assigning an autoreleased object straight to the ivar so there is no guarantee how long it will stay around for. You should be assigning a non autoreleased object or going through the setter
// Going through the setter
self.RGBSpacesDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath1];
// OR
// Non assigning a non autoreleased object
RGBSpacesDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath1];
To use the setter you would have to redeclare the property in an extension at the top of the app delegate's .m file like this
#interface CalculatorAppDelegate ()
#property (nonatomic, retain, readwrite) NSDictionary *RGBSpacesDictionary;
#end
...
The rest of your implementation
Try to retain the dictionary in app delegate. It must be deallocated at some point, because you get an autoreleased one and you didn't use the property to set it.
// here you must retain the dictionary
[[NSDictionary dictionaryWithContentsOfFile:plistPath1] retain];
Of course don't forget to release it later in dealloc.

Override #synthesize method?

I have one class called dog and another class called cat.
Dog has an variable, "name" declared with #property in the .h file.
In the cat class, I have set the name by using the command, myDog.name = "buster", after creating the variable "myDog" of type dog.
I would like to do additional operations when the name is set by overriding the set method normally created by the #synthesize command.
How can I do that? Thanks for the help!
All you have to do is leave the #synthesize then create whichever methods you want to be custom. Example:
In .h
#property(nonatomic, retain)NSString *Bob;
In .m
#synthesize bob;
-(void)setBob:(NSString *)bobValue{
[bobValue retain];
[bob release];
bob = bobValue;
//your custom stuffs here
}
This has been pretty much answered on SO already - see Objective-C synthesize property name overriding for details. In particular, #Dev Kanchen's answer which includes example code.
You cannot override (and call it within) a synthesized method from within the very same class.
You can however override it from a subclass (or rather: synthesize it in an abstract superclass).
If you simply want to perform additional (vs. different) operations upon property change I would use KVO by simply adding each dog as observer to its own "name" property in -(id)init;.
Edit:
There is a way to add additional logic to synthesized methods from within the same class:
Define a private intermediate property in a class extension.
I've attached source code for a class which uses synthesized properties and takes care(sic!) of keeping the dog's owner in sync with its own identity.
Dog.h:
#import <Foundation/Foundation.h>
#interface Dog : NSObject {
#private
NSString *name;
NSString *owner;
}
#property (nonatomic, readwrite, retain) NSString *name;
#property (nonatomic, readwrite, retain) NSString *owner;
#end
Dog.m:
#import "Dog.h"
#interface Dog ()
#property (nonatomic, readwrite, retain) NSString *primitiveName;
#end
#implementation Dog
#dynamic name;
#synthesize primitiveName = name;
#synthesize owner;
- (id)init {
if ((self = [super init])) {
name = #"Snowy";
owner = #"Tintin";
}
return self;
}
- (void)dealloc {
[super dealloc];
}
- (NSString *)name {
return self.primitiveName;
}
- (void)setName:(NSString *)aName {
self.primitiveName = aName;
if ([aName isEqualToString:#"Snoopy"]) {
self.owner = #"Charlie Brown";
}
else if ([aName isEqualToString:#"Snowy"]) {
self.owner = #"Tintin";
}
}
- (NSString *)description {
return [NSString stringWithFormat:#"<%# name:'%#' owner:'%#'>", [self class], self.name, self.owner];
}
#end
Test:
Dog *dog = [[Dog alloc] init];
NSLog(#"%#", dog);
dog.name = #"Snoopy";
NSLog(#"%#", dog);
dog.name = #"Snowy";
NSLog(#"%#", dog);
Result:
<Dog name:'Snowy' owner:'Tintin'>
<Dog name:'Snoopy' owner:'Charlie Brown'>
<Dog name:'Snowy' owner:'Tintin'>

How to obtain UITextFields from a singleton

I need just four parameters to drive the calculations in three view controllers. The parameters come from four UITextFields in the first view. I'm not using global variables but have developed a singleton class to enable the parameters to be available to each controller. I can reference the variables OK in the controllers but I don't know how to initialize them. I've obtained the variables from the first viewcontroller through a windows I developed with IB but can't seem to find a way to get these initialized in the singleton.
Help and guidance gratefully appreciated.
//
// GlobalParameters.h
// ProjectEstimator
//
// This is a SINGLETON class used to handle global parameters for use in the various view controllers.
//
//
//
// Created by Frank Williamson on 10/06/2010.
//
#import <Foundation/Foundation.h>
#interface GlobalParameters : NSObject {
// Place any "global" variables here
// float *processes;
// float *entities;
// float *transactions;
// float *users;
IBOutlet UITextField *noOfProcesses;
IBOutlet UITextField *noOfEntityClusters;
IBOutlet UITextField *noOfTransactions;
IBOutlet UITextField *noOfUserArea;
}
#property (retain, nonatomic) UITextField *noOfProcesses;
#property (retain, nonatomic) UITextField *noOfEntityClusters;
#property (retain, nonatomic) UITextField *noOfTransactions;
#property (retain, nonatomic) UITextField *noOfUserArea;
// message from which our instance is obtained
+ (GlobalParameters *)sharedInstance;
#end
//
// GlobalParameters.m
// ProjectEstimator Singleton for handling (global) parameters.
//
// Created by Frank Williamson on 10/06/2010.
//
#import "GlobalParameters.h"
#implementation GlobalParameters;
#synthesize noOfProcesses;
#synthesize noOfEntityClusters;
#synthesize noOfTransactions;
#synthesize noOfUserArea;
+ (GlobalParameters *)sharedInstance{
// the instance of this class is stored here
static GlobalParameters *noOfProcesses = nil;
static GlobalParameters *noOfEntityClusters = nil;
static GlobalParameters *noOfTransactions = nil;
static GlobalParameters *noOfUserArea = nil;
// check to see if an instance already exists
if (nil == noOfProcesses) {
noOfProcesses = [[[self class] alloc] init];
// **How to I initialize UITextFields from a ViewController in here??**
}
// return the instance of this class
return noOfProcesses;
if (nil == noOfEntityClusters) {
noOfEntityClusters = [[[self class] alloc] init];
// **How to I initialize UITextFields from a ViewController in here??**
}
// return the instance of this class
return noOfEntityClusters;
if (nil == noOfTransactions) {
noOfTransactions = [[[self class] alloc] init];
// **How to I initialize UITextFields from a ViewController in here??**
}
// return the instance of this class
return noOfTransactions;
if (nil == noOfUserArea) {
noOfUserArea = [[[self class] alloc] init];
// **How to I initialize UITextFields from a ViewController in here??**
}
// return the instance of this class
return noOfUserArea;
}
#end
There's a number of things here I would change. First, I wouldn't create a "Globals" class. Second, if I did, would never store UIView objects in class designed to be a "model". Third, if I absolutely needed to implement a singleton, I would follow the design pattern for Cocoa.
I need just four parameters to drive the calculations in three view controllers.
And at that point I recommend you throw out this class entirely and re-think your design problem. Is it simply that you need to store four NSString parameters (or NSNumbers or NSIntegers or floats or...)? If so, it's complete overkill to store the parameters inside a view class. Just create a simple class to store the parameters, whose header would look something like:
#interface MyParameters : NSObject
{
NSString* someString;
NSNumber* someNumber;
NSInteger someInteger;
float someFloat;
}
#property (nonatomic, retain) NSString* someString;
#property (nonatomic, retain) NSNumber* someNumber;
#property (nonatomic, assign) NSInteger someInteger;
#property (nonatomic, assign) float someFloat;
#end
I would instantiate an object of this class either in my application delegate or in my root view controller, and then I would pass it along to any other view controllers that needed access to it. Finally, I would use Key-Value Observing so every view controller that needs it will get automatic updates to any changes to the parameters.