Cannot access public variable of another class - objective-c

I know this question is asked often, I've read so much on it but I still cant get it to work. Lets say I have two classes, FirstClass and SecondClass. FirstClass has a label and SecondClass wants to get the text of that label. Here is what I've done:
//FirstClass
#interface FirstClass : UIViewController
{
#public
UILabel *theLabel;
}
#property (strong, nonatomic) UILabel *theLabel;
#implementation FirstClass
#synthesize theLabel;
//SecondClass
#import "MainGameDisplay.h"
#interface SecondClass : UIViewController
{
MainGameDisplay *mainGame;
}
#property (strong, nonatomic) UILabel *theSecondLabel;
#implementation SecondClass
-(void) thisMethodIsCalled {
mainGame = [[FirstClass alloc] init];
self.theSecondLabel.text = mainGame.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text); //Output is '(Null)'
}
theLabel.Text is not nil as it's being changed every second and is also displaying the label on the other controller which is running in the background whilst the SecondClass view is loaded. Could someone please point me to the write direction if I'm completely wrong, or show me some kind of example as to how this would be done. Thank you.
EDIT:
#Implementation FirstClass
#synthesize theLabel;
- (void)viewDidLoad {
[self superview];
[self startTickCount];
}
-(void) startTickCount {
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(timeChanger) userInfo:nil repeats:YES];
}
-(void) timeChanger {
theDay++;
NSLog(#"%#",self.theLabel.text);
if (theDay <= 9)
self.theLabel.text = [NSString stringWithFormat: #"0%i", theDay];
else
self.theLabel.text = [NSString stringWithFormat: #"%i", theDay];
if (theDay > 27)
[self monthChanger];
}
That's pretty much it. The NSLog outputs the day as expected.

I assume MainGameDisplay is your FirstClass. Then in order to update theSecondLabel.text in you SecondClass object you need to pass an object of FirstClass and not to instantiate it in method call.
I guess you need to do something like this (this is a very simple example)
Add a property to your SecondClass
#property (nonatomic, strong) FirstClass *firstClass;
After that:
1) create instance of FirstClass, let it have name firstClass.
2) create instance of SecondClass. SecondClass *secondClass = [[SecondClass alloc] init];
3) set property of Second class to instance of FirstClass
secondClass.firstClass = firstClass;
4) now you have a reference to actual object of FirstClass and can access its properties.
-(void) thisMethodIsCalled {
self.theSecondLabel.text = self.firstClasss.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text);
}
I hope this will help.

if you didnt leave out LOTS of code,
-(void) thisMethodIsCalled {
mainGame = [[MainGameDisplay alloc] init];
self.theSecondLabel.text = mainGame.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text); //Output is '(Null)'
}
will not work.. nobody can modify mainGame in between the alloc init and the getting of .text variable....
[#all I know this is not an answer, but the formatting of comments sucks. Ill edit or delete it as needed]

If this is your code exactly, you have two problems. First, Text is unnecessarily capitalized. And, secondly, TheLabel is unnecessarily capitalized.
Edited Code:
-(void) thisMethodIsCalled {
mainGame = [[MainGameDisplay alloc] init];
// 'text' shouldn't be capitalized
// 'theLabel' shouldn't be capitalized
self.theSecondLabel.text = mainGame.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text);
}

The method name is text, not Text. The case matters, and using text with a lower T will cause errors.

Related

Issue adding to NSMutableArray

I have looked all over the place for anyone who has experienced this issue but have yet to find anything relevant, so I thought I'd ask it myself...
I have a custom object (HitterData) which I will use to populate cells in a UITableView, then two ViewControllers (one is hitterTableViewController, the other is a "detail" view controller labeled "AddPlayerViewController").
The problem is that I can add HitterData objects to the NSMutableArray in my Table VC, but only one, and then when I add another one using the detail view controller, the Mutable array is "reinitialized" and I can again only have one object at a time.
HitterObject:
#implementation HitterData.m
#synthesize hitterName = _hitterName;
#synthesize position = _position;
-(id)initWIthNameAndPosition:(NSString *)hitterName position:(NSString *)position {
if ((self = [super init])) {
self.hitterName = _hitterName;
self.position = _position;
}
return self;
}
HitterTableViewController.h
#import "HitterData.h"
#import "HitterDoc.h"
#import "AddPlayerViewController.h"
#interface HitterTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *hitters;
- (IBAction)backButton:(id)sender;
- (IBAction)addPlayerView:(id)sender;
-(void)addHitterObject:(HitterData *)hitter;
HitterTableViewController.m (only relevant to make this more readable)
#implementation HitterTableViewController
#synthesize hitters = _hitters;
- (void)viewDidLoad {
[super viewDidLoad];
self.hitters = [NSMutableArray array];
}
-(void)addHitterObject:(HitterData *)hitter {
if(_hitters != nil) {
[_hitters addObject:hitter];
} else {
_hitters = [NSMutableArray array];
[_hitters addObject:hitter];
NSLog(#"MutableArray is not valid");
}
}
AddPlayerViewController.h
#interface AddPlayerViewController : UIViewController <UITextFieldDelegate, UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *positionTextField;
#property (nonatomic) HitterTableViewController *hitterTable;
#property (nonatomic) NSString *hitterName;
#property (nonatomic) NSString *position;
//-(void)addNewHitterToHittersArray:(HitterData *)hitter;
- (IBAction)addPlayerToRoster:(id)sender;
AddPlayerViewController.m
#implementation AddPlayerViewController
#synthesize hitterTable;
- (void)viewDidLoad {
[super viewDidLoad];
hitterTable = [[HitterTableViewController alloc] init];
}
- (IBAction)addPlayerToRoster:(id)sender {
self.hitterName = [self.nameTextField text];
self.position = [self.positionTextField text];
HitterData *hitter = [[HitterData alloc] init];
hitter.hitterName = self.hitterName;
hitter.position = self.position;
[hitterTable addHitterObject:hitter];
ArraySingleton *arrayS = [[ArraySingleton alloc] init];
[arrayS initializeArray];
[arrayS addToHittersArray:hitter];
if (arrayS) {
NSLog(#"arrayS exists in AddPlayerVC");
} else {
NSLog(#"arrayS does not exist");
}
[self performSegueWithIdentifier:#"backToTeamTableViewController" sender:self];
}
Am I missing something here?
Guess based just on the code shown:
Every time you wish to add a player it looks like you create a new AddPlayerView/AddPlayerViewController. In turn that controller creates, in its viewDidLoad, a new HitterTableViewController - which of course has its own empty array. The code should instead be referencing the existing HitterTableViewController.
BTW: The common design pattern is MVC - model, view, controller - consider whether you are in your current situation because you've stored part of your model, the array, in your controller, and maybe both controllers should be referencing a common shared model object containing that array.
BTW: All those #synthesise statements are unneeded. In modern Obj-C synthesis is automatic and you rarely need these statements. Also it is generally recommended to not use the property backing variable directly, and certainly not when storing into the property as this breaks KVO. (There also appears to be a related typo in HitterData.m but as you don't report that as not working it is probably just a typo in your question and not code.)
HTH
AddPlayerViewController should know nothing about HitterTableViewController, return the new HitterData object with a delegate method.
- (IBAction)addPlayerToRoster:(id)sender
{
Hitter *hitter = [[Hitter alloc] init];
hitter.name = [self.nameTextField text];
hitter.position = [self.positionTextField text];
[self.delegate didAddHitter:hitter];
}
Then back in HitterTableViewController
- (void)didAddHitter:(Hitter *)hitter
{
[self.hitters addHitter:hitter];
[self dismissViewControllerAnimated:YES completion:nil];
}

Array loses all values after go trough its own method - Objective C

I have this piece of code below and I'm trying to add Objects(String elements) to an array, problem is that every time I'm out its adding's method, it goes to nil, it doesn't retain the objects.
I know I'm doing wrong, even that I already tried lot of combinations and variations, even with my own constructor _MyArray etc etc, same result... it works, but not further...
Could you help me please?
#interface ArraysModel()
#property (nonatomic, retain) NSMutableArray *MyArray;
#end
#implementation ArraysModel
#synthesize MyArray;
-(void)AddObjectToTheList:(NSString *)object {
if(!MyArray) MyArray = [[NSMutableArray alloc] init];
[MyArray addObject:object];
NSLog(#"%#",self.MyArray);
NSLog(#"Object added %u",[self.MyArray count]);
}
-(NSMutableArray *)ObjectList {
return self.MyArray;
NSLog(#"%#",self.MyArray);
NSLog(#"Object added %u",[self.MyArray count]);
}
#end
The header is like this:
#interface ArraysModel : NSObject
-(void)AddObjectToTheList:(NSString *)object;
And here is my call from my ViewController:
- (IBAction)AddToTheList {
ArraysModel *MyObjectToAdd = [[ArraysModel alloc] init];
[MyObjectToAdd AddObjectToTheList:TextArea.text];
[self.view endEditing:YES];
Well, there's your problem -- you're alloc init'ing a new instance of ArraysModel, and therefore a new array with every call. You need to create a strong reference to your instance, and check for whether it exits, and only init if it doesn't.
In the .h:
#property (strong, nonatomic) ArraysModel *myObjectToAdd;
in the .m:
-(IBAction)AddToTheList {
if (! self.myObjectToAdd) {
self.myObjectToAdd = [[ArraysModel alloc] init];
}
[self.myObjectToAdd AddObjectToTheList:TextArea.text];
[self.view endEditing:YES]
}

Can't get an objective-c object to respond to a message. What am I missing?

I'm trying to teach myself Objective-C and as an exercise, I'm trying to write an app with one button and one label. When I click on the button, I want to trigger a calculation then see the results in the label. The following code compiles and runs with no errors or warnings but as far as I can tell, the [object method] 'call' doesn't do anything. I've spent hours on this and just don't see what's wrong. Can anyone explain the problem? Thanks.
*** testMethodViewController.h ****
#import <UIKit/UIKit.h>
#import "testBrain.h"
#interface testMethodViewController : UIViewController
{
IBOutlet UILabel *display;
testBrain *model;
}
- (IBAction)cellPressed:(UIButton *)sender;
#end
*** testMethodViewController.m ****
#import "testMethodViewController.h"
#implementation testMethodViewController
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
- (IBAction)cellPressed:(UIButton *)sender
{
int x = [model check:3]; //This method call doesn't work. But gets no errors.
NSLog(#"Results from model: %i", x); //Says x = 0, but I expect 6
NSString *xAsString = [NSString stringWithFormat: #"testBrain: %i", x];
display.text = xAsString; //Label is updated and displays: testBrain: 0
} //I expect: testBrain: 6
#end
*** testBrain.h ****
#import <Foundation/Foundation.h>
#interface testBrain : NSObject {}
- (int) check:(int) anInteger;
#end
*** testBrain.m ****
#import "testBrain.h"
#implementation testBrain
- (int) check:(int) anInteger //3 passed as the parameter.
{
int r = anInteger + anInteger;
NSLog(#"inside check %i", r); //Debugging line: doesn't print.
return r;
}
#end
When this code runs:
int x = [model check:3];
model is nil. In Objective-C, messages sent to nil silently do nothing, and return 0. So, as you see, x is 0 and -check: is never called.
Apparently you were expecting this method to be called automatically:
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
However, that method will be called only if you do it yourself, by saying [self model] or self.model. So, this line would fix it:
int x = [[self model] check:3];
Try it and see.
Going a little further: It would be clearer to remove the model method entirely, and create the instance variable model when the UIViewController is created. That way, we can guarantee that model is valid anytime any code in the testMethodViewController class runs.
You would do that by overriding UIViewController's designated initializer:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Now you can initialize your instance variables
model = [[testBrain alloc] init];
}
return self;
}
With your model method, you are halfway towards Lazy Instantiation, however to properly achieve this, you must always acess the lazily instantiated object through its accessor method. You aren't doing this in your button action, so your messages are going to nil, which is silently ignored.
This is one of the reasons you often see instance variables in objective-c declared with a leading or trailing underscore. If you then typed model anywhere in the rest of your class, it would be a compiler error, forcing you to use the accessor. Typically this is implemented with properties and the synthesize statement:
In your interface:
#property (nonatomic, strong) TestBrain* model;
In your implementation:
#synthesize model = model_;
Your model method would be:
-(TestBrain*)model
{
if (!model_)
model_ = [[TestBrain alloc] init];
return model_;
}
You would then use self.model instead of model throughout the rest of the class.
If you are just starting out, the Stanford iOS course on iTunes U is an excellent resource, a lot of this sort of material is covered.
int x = [model check:3];
This line should be:
int x = [self.model check:3];
you are almost there. You need to use #property and #synthesize in order to complete this. The #synthesize directive will direct the compiler to create the setters and getters for a particular property. The #synthesize directive tells the compiler that variable is a property. Properties allow you to use the dot syntax. i.e. self.model which will automatically the call the getter or setter method, depending on the context.
In your testMethodViewController.h file change it to look like this:
#interface testMethodViewController : UIViewController
{
IBOutlet UILabel *display;
testBrain *model;
}
#property (nonatomic,retain) testBrain *model;
- (IBAction)cellPressed:(UIButton *)sender;
#end
then in the .m implementation you need to use #synthesize after the #implementation. Like this:
#implementation testMethodViewController
#synthesize model; // tells the compiler to synthesize the setter and getter for you
- (testBrain *)model
{
if (!model) {model = [[testBrain alloc] init];}
return model;
}
then in your cellPressed: method, you need to use self.model in order for the getter to be called:
- (IBAction)cellPressed:(UIButton *)sender
{
int x = [self.model check:3]; //This method call doesn't work. But gets no errors.
NSLog(#"Results from model: %i", x); //Says x = 0, but I expect 6
NSString *xAsString = [NSString stringWithFormat: #"testBrain: %i", x];
display.text = xAsString; //Label is updated and displays: testBrain: 0
}
Hope this helps.
I dont see anywhere in the testMethodViewController.h file
IBOutlet UIButton *button;
Also check if u have properly connected all IBOutlet, IBAction & delegate, datasource.

Instance variable does not retain its value

I'm learning Objective-C right now and in order to practice I wrote a simple random maze generator for OS X, which works fine. Next I tried to add some more interaction with buttons, but I'm having trouble with the instance variables as they don't retain the value I assign them. I have come across multiple questions about the same problem, but the solutions to those haven't solved my problem. I also tested if the same problem persists in a simplified version of the program, which it does.
I guess I'm doing something wrong, but I don't know what. Here's what I did:
Created a new project
Added a subclass of NSView called "TestClass"
Added a view with class TestClass in the window in MainMenu.xib
Added an object for TestClass in MainMenu.xib
Added a button to the view and set its tag to 1
Added the following code to TestClass.h and TestClass.m and connected the button to it:
TestClass.h:
#import
#interface TestClass : NSView
{
NSNumber *number;
NSButton *test;
}
#property (nonatomic, retain) NSNumber *number;
#property (assign) IBOutlet NSButton *test;
- (IBAction)testing:(id)sender;
#end
TestClass.m:
#import "TestClass.h"
#implementation TestClass
#synthesize number;
#synthesize test;
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (IBAction)testing:(id)sender
{
self.number = [[NSNumber numberWithLong:[sender tag]] retain];
}
- (void) drawRect:(NSRect)dirtyRect
{
NSLog(#"%#", number);
}
#end
Whenever I press the button, NSLog just returns null several times.
I normally figure out everything by myself (eventually...), but this time it's really driving me insane, so is there anyone who can help me?
Put the NSLog in testing:, or just put a breakpoint there and see what's stored in number.
Note that self.number = [[NSNumber numberWithLong:[sender tag]] retain]; is double-retaining the NSNumber object (which is wrong), but that shouldn't cause any immediate error.

After setting one NSManaged object to another it returns Null

I am trying to pass the selected object in my coredata from the rootviewcontroller to the edit view. The selected object is being passed but is then becoming null after the theObject=selectedObject is being called. Anyone know what im doing wrong?
This is in the edit.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "LearningAppDelegate.h"
#interface edit : UIViewController <UITextViewDelegate, UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate>{
UITableView *tableView;
NSManagedObject *theObject;
UITextView *messageView;
}
#property(nonatomic, retain) IBOutlet UITableView *tableView;
#property(nonatomic, retain) IBOutlet UITextView *messageView;
#property(nonatomic, retain) NSManagedObject *theObject;
-(id)initWithObject:(NSManagedObject *)selectedObject;
#end
This is in the edit.m:
-(id)initWithObject:(NSManagedObject *)selectedObject {
self = [super init];
if (nil == self) {
return nil;
}
NSLog(#"selectedObject: %#", selectedObject);
NSLog(#"selecetedObject.message: %#", [[selectedObject valueForKey:#"message"] description]);
theObject=selectedObject;
NSLog(#"theObject 1: %#", theObject);
NSLog(#"theObject.message 1: %#", [[theObject valueForKey:#"message"] description]);
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"theObject 2: %#", theObject);
NSLog(#"theObject.message 2: %#", [[theObject valueForKey:#"message"] description]);
messageView.text=[[theObject valueForKey:#"message"] description];
[super viewDidLoad];
}
I am actually amazed that doesn't crash for you. You're assigning the variable selectedObject into the instance variable theObject without retaining it for your own use. By accessing the instance variable directly in the assignment 'theObject=selectedObject', you're bypassing the behavior granted by the #property declaration. This means that once selectedObject is finally dealloc'd, theObject will point to garbage memory.
The correct way to do this is to put theObject = [selectedObject retain]; in the -initWithObject: method and in -viewDidLoad to access it via self.theObject rather than just theObject.
In my own usage I prefer to give instance variables names different from the actual property name to avoid confusion. For example:
#interface SomeClass : NSObject
{
#private
NSManagedObject *_theObject;
}
#property (nonatomic, retain) NSManagedObject *theObject;
...
#end
#implementation SomeClass
#synthesize theObject = _theObject
...
- (void)dealloc
{
[_theObject release], _theObject = nil;
[super dealloc];
}
#end
I only have experience with Coredata on the desktop, but the problem looks like it would be with your initWithObject method. At no point do you actually insert the new object into the managed object context. You should be using this method to make new objects:
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
As an example in pseudocode:
NSManagedObject *newObject = [[NSManagedObject alloc] initWithEntity:NSENTITYDESCRIPTION insertIntoManagedObjectContext:MANAGEDOBJECTCONTEXT];
[newObject setValue:#"VALUE_OF_SELECTED_OBJECT" forKey:#"APPROPRIATE_KEY"];
//REPEAT AS NECESSARY
[MANAGEDOBJECTCONTEXT save];
*Code not tested, naming conventions are ignored, etc.
The save is important. If you don't do this the object won't persist.