Using Custom Subclass of NS Object in 2 different IBActions - iphone

This is an objective-c problem. I have created a subclass person of NSObject with parameters 'height' and 'weight', with property and synthesize in a file called Person.h that contains both interface and implementation.
I want to import Person.h into my viewcontroller.m and create person objects and alter them using 2 IBActions.
-(IBAction)alterperson_1{
person *bob = [person alloc]init];
bob.height = 72;
bob.weight = 200;
}
-(IBAction)alterperson_2{
bob.height = 80;
bob.weight = 250;
}
This arrangement does not work because the method alterperson_2 can't find Bob because it is a local variable to alterperson_1. My question is how and where in viewcontroller.m do I allocate Bob as a person so that his attributes can be altered by both IBActions.
I have tried allocing in viewdidload as well as in the initwith nibname methods. It did not work. I have also tried in the implementation{ } of viewcontroller.m but that doesn't work either because Bob's allocation is not a compile time constant.
Thanks!
Update With Code
So, I have the Person.h file importing properly now (thanks Robotnik), and am able to create instances of Person throughout ViewController.m -- however, my created instance *bob does not seem to retain values for its properties (see comments by NSLog statements in the code). I think this is an initialization issue, but I have no idea where to initialize. Currently, I get a warning when initializing in viewDidLoad. How do I get bob.weight to print 200 when my IBAction is called, instead of the 0 I currently get? Thanks.
// Person.h
#import <Foundation/Foundation.h>
#interface Person : NSObject{
int weight;
int height;
}
#property int weight, height;
#end
end Person.h
//Person.m
#import "Person.h"
#implementation Person
#synthesize weight, height;
#end
end Person.m
//ViewController.h
#import <UIKit/UIKit.h>
#import "person.h"
#interface ViewController : UIViewController{
}
#property Person *bob;
-(IBAction)persontest:(id)sender;
#end
end ViewController.h
//ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize bob;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *bob = [[Person alloc]init]; // this causes a local declaration warning, if I remove this code, however, it still doesn't work
bob.weight = 100;
NSLog(#"viewDidLoad bob's weight, %i", bob.weight); // this will print 100, but only because I made the local initialization. The value is lost once the viewDidLoad Method ends.
}
-(IBAction)persontest:(id)sender{
bob.weight = bob.weight + 100;
NSLog(#"IBAction bob's weight %i", bob.weight); // this prints 0, probably because value is nil. How can I make it print 200?
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
end ViewController.m

You'll need to declare Bob in your ViewController.h if you want him to be accessible across multiple methods. You can then initialise him in viewDidLoad
#interface ViewController
{
Person *bob;
}
-(IBAction)alterperson_1;
-(IBAction)alterperson_2;
#end
You mentioned you wanted to instantiate multiple people. In that case you may want to keep multiple Person objects in an NSMutableArray or similar. This should still be declared in the ViewController.h in order to be accessible in multiple methods. You can then use the method addObject: to add people to the array. Here is an example of an array, storing strings.
NSMutableArray *stringArray = [[NSMutableArray alloc] init];
[stringArray addObject:#"Dog"];
[stringArray addObject:#"Cat"];

You seem to be confusing declaration of a variable with allocation of a variable. You're also not using properties properly.
Declaration of a variable is done like this:
Person *bob;
That's it. If you put this line in your #interface section, or in {braces} at the top of your implementation section, then this becomes an instance variable, any time you use bob in the rest of your class it will know that you are talking to a person. So, in your viewDidLoad, you can now do this:
bob = [[Person alloc] init];
And it knows you are referring to the instance variable. In your current code, you have Person * in front of this line, which, as the compiler is telling you, is declaring a local variable with the name bob, and therefore hiding your instance variable, so you are not changing your instance variable at all, so there is no value in it later.
Looking at the nature of this question, and your comments, I would strongly advise reading some objective-c introductory texts before proceeding much further. You can ask questions here but SO is not really the place to learn a language - most answerers will assume that you know the language basics.

Related

How to access a method in an object within another object, from the outside?

I am in ViewController, trying to access a method in object "cat" owned by object "backgroundpicture". ViewController has an instance of backgroundpicture.
The method/message in "cat.h":
#interface Cat : NSObject
-(BOOL)checkIfTouchHit:(float) xx :(float) yy;
#end
"Cat.m":
- (BOOL)checkIfTouchHit:(float) xx :(float) yy{
NSLog(#"Inside checkIfTouchHit");
return YES;
}
"BackGroundPicture.h":
#import "Cat.h"
#interface BackGroundPicture : NSObject
#property (strong) Cat * katt;
#end
"BackGroundPicture.m":
#implementation BackGroundPicture
#synthesize katt = _katt
#end
"ViewController.m":
#interface ViewController ()
#property (strong) BackGroundPicture * bakgrunnsbilde;
#end
#implementation BackGroundPicture
#synthesize bakgrunnsbilde = _bakgrunnsbilde;
- (void)viewDidLoad
{...
[[self.bakgrunnsbilde katt] checkIfTouchHit :(float)touchLocation.x :(float)touchLocation.y]
...}
The string inside the method "checkIfInside" in cat will not show up. I also tried
[_bakgrunnsbilde katt]...
but with the same lack of result, and I believe this is compiled the same way. I am wondering what I am missing here, and hope someone can help. Thanks :)
edit I forgot to add a few lines from my BackGroundPicture.m. It is a method run on start from the ViewDidLoad in ViewController. It is like this in BackGroundPicture.m:
- (void)createObjects {
Cat * katt = [[Cat alloc] init];
}
it is called from ViewController.m like so:
- (void)viewDidLoad
{
[_bakgrunnsbilde createObjects];
}
I know that this get executed. I hope this edit makes sense, my head is ruined after a long day :) Going to check back tomorrow morning.
It will work, but BackGroundPicture.m needs to allocate a cat first.
So in BackGroundPicture.m, do this:
- (id)init {
self = [super init];
if (self) {
_katt = [[Cat alloc] init];
}
return self;
}
In general, remember to allocate objects before you use them. You may also need to create a BackGroundPicture, too as Valentin points out. In viewDidLoad, do this:
bakgrunnsbilde = [[BackGroundPicture alloc] init];
As far as I can see you're accessing the method correctly. You could use the property, for readability sake (you also don't need the cast)
[self.bakgrunnsbilde.katt checkIfTouchHit:touchLocation.x :touchLocation.y]
, but your way of doing it should work as well.
You should check if your -viewDidLoad method gets called and if self.bakgrunnsbilde or self.bakgrunnsbilde.katt is not nil when -viewDidLoad gets called. One of this should get you on the right track.

using arc, my ivars are null after init

MOST NEW TESTING:
I placed a NSLog(#"%p", self.myArray);
after the array assignment and I am seeing a address actually logged.....!?!?!
2012-03-06 01:33:52.618 ArrayTest[9883:f803] 0xae0f160
Seems that Xcode is all wacked out if it cant see the addess of that ivar in either local variables or with the tool tip highlight method...
Thoughts?
NEWEST TESTING:
I created a brand new project.
It seems that simple assigning of objects to ivars is not working at all. If I look at the address of myArray after the assignment of the newly created array it has a null address.
output of nslog
2012-03-06 01:30:37.283 ArrayTest[9848:f803] (
)
(lldb)
//
// ViewController.h
// ArrayTest
//
// Created by Ben J Brown on 3/6/12.
// Copyright (c) 2012StudioBflat. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
NSMutableArray *myArray;
}
#property (strong) NSMutableArray *myArray;
#end
//
// ViewController.m
// ArrayTest
//
// Created by Ben J Brown on 3/6/12.
// Copyright (c) 2012 StudioBflat. All rights reserved.
//
#import "ViewController.h"
#implementation ViewController
#synthesize myArray;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableArray *array = [NSMutableArray arrayWithCapacity:16];
self.myArray = array;
NSLog(#"%#",self.myArray);
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
OLDER DATA:
In my viewDidLoad I have:
NSLog(#"%#",self.collectionOfImageViews);
self.collectionOfImageViews = [[NSMutableArray alloc] initWithCapacity:NUMBER_OF_COLUMNS*NUMBER_OF_ROWS];
NSLog(#"%#",self.collectionOfImageViews);
However later on when I access that array the array has an address but all the objects that I added to it are gone, and when I send a count message to that object(the NSMutableArray) it throws this in the console:
-[UIImage count]: unrecognized selector sent to instance 0x6b7ab40
My properties in my interface:
#property(strong) NSMutableArray* collectionOfImageViews;
and I have #synthesize collectionOfImageViews; right after my #implmentation... what am I missing here?
Here is where I make the collection:
NSLog(#"%#",self.collectionOfImageViews);
self.collectionOfImageViews = [[NSMutableArray alloc] initWithCapacity:NUMBER_OF_COLUMNS*NUMBER_OF_ROWS];
NSLog(#"%#",self.collectionOfImageViews);
looking at the array it has a null address right after this action....
Concerning that earlier weird error where I had it consoling out that it was a UIImage not responding to count... I fixed that kinda by changing the order of the ivar declarations in the interface:
#import <UIKit/UIKit.h>
#import "TiledImageView.h"
#interface ViewController : UIViewController <TiledImageViewDelegation>
{
NSMutableArray *collectionOfImageViews;
UIImage *sourceImage;
UIImageView *currentTappedView;
}
#property(strong) NSMutableArray* collectionOfImageViews;
#property(strong) UIImage* sourceImage;
#property(strong) UIImageView* currentTappedView;
#end
As for where I fill the mutable array later here is that code:
iView = [[TiledImageView alloc] initWithImage:[UIImage imageWithCGImage:tempImage]];
iView.userInteractionEnabled = YES;
iView.delegate = self;
iView.contentMode = UIViewContentModeScaleAspectFit;
[iView setTag:index];
[iView setPosX:column];
[iView setPosY:row];
[collectionOfImageViews addObject:iView];
I'm pretty darnd confused because this is simple ivar setting and getting.. and alloc and initialization... something I have done many times before but it seems my ivars are not staying alive... I'm new to ARC.
This seems to be an LLDB bug. Use GDB for debugging instead.
edit: It obviously is a bug within lldb, i have filed a bug report back in march (it was marked as a duplicate of another bug which was later marked as closed) - the issue has been resolved in newer Xcode versions.
The fact that u get () means that there is nothing wrong with the array. You can test it easily by adding any other object like an NSNumber and then logging the array again, you should see your object there. About the memory address, If you are using the debugger you should set the break point AFTER the instruction where the array is allocated, then you will be able to see the contents of the array. Otherwise if your break point is not there who knows what you will be seeing.
Finally the -[UIImage count]: unrecognized selector sent to instance 0x6b7ab40
Means you are trying to make an UIImage object perform a count method, which doesn't exist, You probably want the count on the array not the object inside. So the problem is in where you are calling this method, you seem to have a hard time when it comes to referencing either the object or the ivar, to avoid this, on the synthesize change the #synthesize collectionOfImageViews; to #synthesize collectionOfImageViews = collectionOfImageViews;
Also try changing the compiler like others have suggested.

How to work with inheritance in objective-C (iOS sdk)

I just started to learn iOS programming and I have a problem with inheritance. There are 2 files.
First file
Header
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
int x;
}
#end
Implementation:
#import "ViewController.h"
#import "NewClass.h"
#implementation ViewController
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
x = 999;
NewClass *myClass = [[[NewClass alloc] init] autorelease];
}
#end
Second file
Header:
#import "ViewController.h"
#interface NewClass : ViewController
#end
Implementation:
#import "NewClass.h"
#implementation NewClass
-(id)init {
self = [super init];
if (self != nil) {
NSLog(#"%i",x);
}
return self;
}
#end
In ViewController I set x to 999, and in NewClass I want to get it, but when I call NSLog(#"%i",x); it gives me 0.
Where did I make a mistake?
You have a timing problem.
The init method gets called first (at all levels of the inheritance hierarchy, so in both ViewController and NewClass). This is when you print out your value of x, when it is still zero.
The viewDidLoad method only gets called much later, generally at a point after a view controller's view has been added to a superview. It's functionality that's specific to the UIViewController class.
To make your example work, put an init method in your ViewController class that looks like the one in your NewClass class, and set x there.
Also, you don't need to create a NewClass instance within ViewController. When you create a NewClass object, it is automatically a ViewController as well. In the same way that a dog is an animal automatically, and so is a cat. When you create a dog, you don't want to create an animal as well!
As sidyll says, you should probably do a bit more reading about how inheritance works, I'm afraid!
You need to review you OOP concepts. Object-Oriented Programming with Objective-C is a must.
Your class NewClass indeed inherits the x variable, but not it's value. When you create an instance of it, you're creating a shiny new instance whose values have nothing to do with the parent class.
Another point of view to help you is that x was set in a object of ViewController class. The NewClass inherits from ViewController class, not from an arbitrary instance (object, where you set x).
That's because -viewDidLoad is not called until well after -init returns. Your superclass should do configuration like that in its -init method.

How to let one class change a variable (or object) in another class

I'm fairly new to programming in Objective-C. While I have been able to find my way, there now is an issue I cannot solve, which is either caused by a mistake I made or because I have a fundamental misunderstanding about classes.
Essentially, I want one class to change a variable (or object) in another class. Here is the code I have:
// LocationManager.h
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface LocationManager : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
CLLocation *locationByCustomLocation;
}
#property (nonatomic, retain) CLLocation *locationByCustomLocation;
#end
Of course, there's a corresponding implementation file: LocationManager.m. It synthesizes the locationByCustomLocation variable.
The point is that from another class, I'd like to manipulate the locationByCustomLocation variable.
// viewCustomLocation.h
#import <UIKit/UIKit.h>
#interface viewCustomLocation : UIViewController <UITableViewDataSource, UITableViewDelegate> {
UITableView *tblLocation;
UITableViewCell *cell;
}
--
//viewCustomLocation.m
#import "viewCustomLocation.h"
#import "LocationManager.h"
#class LocationManager;
#implementation viewCustomLocation
#synthesize tblLocation;
#synthesize cell;
// some view related selectors here, but it boils down to this one:
- (void)dismissView:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
LocationManager *locationManager = [[LocationManager alloc] init];
// I made sure with NSLog that the customLoc variable contains the expected data
CLLocation *customLoc = [[CLLocation alloc] initWithLatitude:place.coordinate.latitude longitude:place.coordinate.longitude];
[locationManager setLocationByCustomLocation:customLoc];
}
Now, if I use NSLog in LocationManager.m to see what's in the LocationByCustomLocation variable, I would expect the same data as in customLoc. Instead, the variable still seems empty.
I think the problem is that I created a copy of the LocationManager class, thus filling the LocationByCustomLocation variable in the copied class, rather than the original one, which is what I want. I can't figure out how to talk to the original LocationManager class.
I know of a few ways to work around this issue, but I would like to know how to achieve it this way to improve my fundamental understanding of the language.
Thanks for reading!
That's because you are allocating a new instance of LocationManager. You can either connect the two controllers between them, like declaring properties and setting them accordingly.
For example, if you instantiate controller B from controller A, you should implement a property for controller B, like firstController, so :
B *controller = [[B alloc] init];
controller.firstController = A;
and then from inside B controller, you control what happens in controller A
An alternate way is to instantiate and control everything from the ApplicationDelegate. It's a more powerful pattern.

NSMutableArray count keeps changing

I have too much code to know which i need to quote here, but in my app delegate I have an NSMutableArray. Then in another class, it creates a new entry to the NSMutableArray but upon passing back to another class which should use that to display something on screen, it doesn't display anything. Putting an NSLog for the NSMutableArray count at the end of the class creating it displays the number 1, and then putting the same NSLog code at the start of the class which is meant to use that returns 0.
Any ideas why this is?
EDIT: Ok, i'll try and include all related code..
app delegate.h:
#interface palettesAppDelegate : NSObject <UIApplicationDelegate> {
NSMutableArray *colourPalettesContainer;
}
#property (assign, readwrite) NSMutableArray *colourPalettesContainer;
#end
app delegate.m:
#import "palettesAppDelegate.h"
#implementation palettesAppDelegate
#synthesize colourPalettesContainer;
- (void)dealloc {
[colourPalettesContainer release];
[super dealloc];
}
#end
Homeview.h:
#import <UIKit/UIKit.h>
#import "HandlingPalettes.h"
#interface HomeView : UIViewController {
HandlingPalettes *handlingPalettes;
}
#end
Homeview.m:
#import "HomeView.h"
#import <QuartzCore/QuartzCore.h>
#implementation HomeView
- (void)viewDidLoad {
[super viewDidLoad];
handlingPalettes = [[HandlingPalettes alloc] init];
[handlingPalettes newPalette];
}
-(void)viewWillAppear:(BOOL)animated {
NSLog(#"view will appear: %i", [dataCenter.colourPalettesContainer count]);
int numberOfExisting = [dataCenter.colourPalettesContainer count];
}
- (void)dealloc {
[handlingPalettes release];
[super dealloc];
}
#end
HandlingPalettes.h:
#import <UIKit/UIKit.h>
#interface HandlingPalettes : UIViewController {
}
-(void)newPalette;
#end
HandlingPalettes.m:
#import "HandlingPalettes.h"
#import "HomeView.h"
#import "palettesAppDelegate.h"
#implementation HandlingPalettes
-(void)newPalette {
palettesAppDelegate *dataCenter = (palettesAppDelegate *)[[UIApplication sharedApplication] delegate];
//If this is the first palette
if (dataCenter.colourPalettesContainer == nil) {
dataCenter.colourPalettesContainer = [[NSMutableArray alloc] init];
}
//Add a new palette
[dataCenter.colourPalettesContainer addObject:#"Test1", #"Test2", nil];
NSLog(#"Handling: %i", [dataCenter.colourPalettesContainer count]);
}- (void)dealloc {
[super dealloc];
}
#end
Your main mutablearray is in your app delegate. So, see what happens if in EVERY METHOD that you want to access the array you have the line to set up the app delegate relationship
palettesAppDelegate *dataCenter = (palettesAppDelegate *)[[UIApplication sharedApplication] delegate];
Now, when you call the dataCenter object you will be referencing the App Delegate and your program will find the array.
You may also find that you will need to have an #import "palettesAppDelegate.h" in each object that is going to reference the App Delegate.
Note, just adding the app delegate code is not necessarily the proper way to deal with this issue from an architectural standpoint. But if it works you at least know the answer to your original question.
I suspect the problem is ultimately related to confused memory management of the colourPalettesContainer member. You release it in the app delegate's dealloc method, but that class never retains it! It would be much cleaner if you'd follow Apple's memory management guidelines: your classes should only release objects that they own (i.e., that they themselves retained earlier). For example, you can do this by declaring the array's property retain:
#property (retain) NSMutableArray *colourPalettesContainer;
(To prevent leaking the array, you'll also need to release or autorelease it in the newPalette method. Retain and release should always come in close pairs.)
But even better, why not simply create the array in the app delegate's init method, or in its accessor (if for some reason you want to continue creating it only on its first use)? Unless you want to replace all palettes at once, there is no reason to let the array be assigned to from outside the app delegate.
#interface PalettesAppDelegate : NSObject <UIApplicationDelegate> {
#private
NSMutableArray *colourPalettesContainer;
}
#property (readonly) NSMutableArray *colourPalettesContainer;
#end
#implementation PalettesAppDelegate
- (NSMutableArray *)colourPalettesContainer {
if (colourPalettesContainer == nil) {
colourPalettesContainer = [[NSMutableArray alloc] init];
return colourPalettesContainer;
}
- (void)dealloc {
[colourPalettesContainer release];
[super dealloc];
}
#end
To make the design even cleaner, change the type of the colourPalettesContainer property to NSArray * and add an -addPalette: method to the app delegate. (It is rarely a good idea to publicly expose a mutable array inside a class.) You can then simply get rid of -newPalette in HandlingPalettes. (If you want to have all your palette-handling methods in HandlingPalettes, then simply move the array there. If you need to access the palettes from random places in your app, then you can simply put a retained reference to your HandlingPalettes object in the app delegate.)
Once you clean up the object ownership mess, the count mismatch will either resolve itself "by magic" or the cause will likely become much more obvious. In the latter case, check that the HomeView's dataCenter is actually the same object as the one in HandlingPalettes. (You omitted how HomeView gets its reference — are you sure you aren't creating another instance of the app delegate by accident?)
(By the way, you probably meant to use -addObjects:, not -addObject: in newPalette. Note also that all class names should be capitalized, with no exceptions: i.e., always use PalettesAppDelegate, never palettesAppDelegate. If for some reason Xcode's project template created it like that, simply rename the class. Lowercase class names are much too easy to confuse with variable names. Also, try to find better names in general: e.g., instead of HandlingPalettes, I'd use PalettesViewController (to reflect the fact that it is a subclass of UIViewController); and instead of dataCenter, I'd rather just choose appDelegate.)
I would be inclined to get rid of the newPalette method, and instead create a getter method for colourPalettesContainer in your app delegate.
ie:
appdelegate.h
#interface PalettesAppDelegate : NSObject <UIApplicationDelegate> {
NSMutableArray *colourPalettesContainer;
}
#property (non-atomic, retain) NSMutableArray *colourPalettesContainer;
#end
#implementation palettesAppDelegate
appdelegate.m
#import "appdelegate.h"
#synthesize colourPalettesContainer;
- (NSMutableArray *) colourPalettesContainer{
if(colourPalettesContainer==nil){
colourPalettesContainer=[[NSMutableArray alloc] init];
}
return colourPalettesContainer;
}
- (void)dealloc {
[colourPalettesContainer release];
[super dealloc];
}
#end
you should then be able to add items by calling
[appDelegate.colourPalettesContainer addObject:object];