Getting an error while trying to access property from an extern Controller in Xcode 4.2 - objective-c

In a first controller, I create an instance variable for my model, because I want to edit its content from a controller and then access the same object from another controller.
In the second controller, I get an error when trying to access the object:
Property 'levels' not found on object of type FirstController.
Model.h
#imports....
#property (readwrite,copy) NSMutableString *answersString;
FirstController.h
#import <UIKit/UIKit.h>
#import "Model.h"
#interface FirstController : UIViewController{
// some declarations
}
#property (nonatomic, retain) LevelsCompleted *levels;
#end
FirstController.m
#import "FirstController.h"
#interface FirstController(){
//stuff
}
#end
#implementation FirstController
#synthesize levels;
//stuff
- (IBAction)backButton:(id)sender { // This is the changeAnswerString method
if (levels ==nil) self.levels = [[LevelsCompleted alloc]init];
self.levels.answersString=#"1";
[self dismissModalViewControllerAnimated:NO];
}
#end
SecondController.m
#import "SecondController.h"
#import "FirstController.h"
#interface SecondController(){
//stuff
}
#end
#implementation SecondController
-(void) viewWillAppear:(BOOL)animated{
NSLog(#"%#",FirstController.levels.answersString);
// the line above gives me the error "Property 'levels' not found on object of type FirstController
}
#end
Can someone tell me what I am doing wrong here? I have tried to create a FirstController object in the SecondController.h, but this does not give me the same property and hence I do not get the right value of the NSString I modified in the first view.

levels is a instance variable so you cannot access it without instantiating an object first.
You should do something like
FirstController *controller = [[FirstController alloc] initWithNibName:#"First" bundle:nil];
NSLog(#"%#",controller.levels.answersString);
[controller release]

You cannot access another viewcontroller from current viewcontroller directly. Define Level in AppDelagte method and then you can access it from anywhere.

What about moving/adding the LevelsCompleted *levels property to the secondviewcontroller and fill SecondViewcontroller.levels.answerstring when you use backbutton: in you first controller?

As a advice try NSUSERDEFAULT to access it,.,

By Doing Things As Below you can Achive as You want
Ddeclare NSMutableString As in your viewController Class As Global variable.
1) LevelsCompleted.h Class
#import <UIKit/UIKit.h>
NSMutableString *answersString;// In this way this answersString would accessible through out the Application and No Need to make property & synthesiz answersString .
#interface LevelsCompleted : UIViewController{
}
LevelsCompleted.m Class
//First create that NSMutableString object in its LevelsCompleted.m class
#import"LevelsCompleted.h"
#interface LevelsCompleted
-(void)viewDidLoad{
answersString=[NSMutableString alloc]init];//here created answersString object
}
#end //end of LevelsCompleted
2)FirstController.h class
#import <UIKit/UIKit.h>
extern NSMutableString *answersString;
#interface FirstController : UIViewController{
// some declarations
}
#end
FirstController.m class
#import "FirstController.h"
#implementation FirstController
- (IBAction)backButton:(id)sender {
// Because i have created that answersString Global in LevelsCompleted.h class
//we can directly Access and can set the any string Value to that answersString as Below
answersString=#"1";
[self dismissModalViewControllerAnimated:NO];
}
#end
SecondController.h class
extern NSMutableString *answersString;// please do this carefully fro getting access the answersString instance
#interface SecondController:UIViewController{
//stuff
}
#end
SecondController.m class
#implementation SecondController
-(void) viewWillAppear:(BOOL)animated{
NSLog(#"%#",answersString);//here you may access easily.
}
#end
In above code everything would work because i have done the same thing in my app
just try to catch the concept of extern, global variable .

Related

How to change a property class in sub class in Objective-C

I am trying to re-write some code of old project, and ideally I want to achieve below code style, but I got a lot of compile error saying dataModel don't have getLineColor method.
The abstract question is, can I change an inherited object A's class in sub view controller to a sub class A2, while the object A in parent view controller is class A1 that is the super class of A2, and how can I do it correctly? Thank in advance.
Update: I compile it, but I have met another run time error, I tried to overwrite the setter of dataModel in sub view controller. How to correctly write the setDataModel in sub class?
#implementation SubViewController
#pragma mark - setter of dataModel
- (void)setDataModel:(ChartModel *)dataModel { // it stucks at this func name
#end
error trace is
[SubChartViewController setDataModel:](self=0x00000000, _cmd=0x00000000, dataModel=0x00000031) + 12 at BDPAxisChartViewController.m:295, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0xbf774ffc)
EDIT:
#interface ChartModel : NSObject
-(BOOL)chartModelhasData;
#end
#interface LineChartModel : chartModel
-(void)getLineColor;
#property (nonatomic, strong) NSArray* seriesNameArray;
#end
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
-(void)layoutChartCanvas;
#end
#implementation ChartViewController
-(void)layoutChartCanvas {
if ([self.dataModel chartModelhasData]) {
self.view.hidden = NO;
}
}
#end
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
-(void)drawLine;
#end
#implementation LineChartViewController
-(void)drawLine {
UIColor *color = [self.dataModel getLineColor];
[self drawLine];
NSArray *tempArray = [self.dataModel.seriesNameArray copy];
}
#end
Probably you want to say:
Can I change the class C of a property already declared in a base class in a subclass to a subclass of C?
This is the set-up
#interface PropertyClass : NSObject
#end
#interface PropertySubclass : PropertyClass
- (void)method; // Additional method
#end
#interface HolderClass : NSObject
#property PropertyClass *property; // Property is of base class
#end
#implementation HolderClass
#end
#interface HolderSubclass : HolderClass
#property PropertySubclass *property; // Property is of subclass
#end
I have no problem to access the property subclass' method in the holder's subclass:
#implementation HolderSubclass
- (void)useIt
{
[self.property method]; // No error or warning
}
#end
In addition to my comment below, I suspect that something like this happens:
// Create a subclass' object
HolderSubclass *object1 = [HolderSubclass new];
…
// Refer this object from a reference that is typed to HolderClass
// **This applies to all usages of self inside #implementation HolderClass**
HolderClass *object2 = object1; // Of course more complex
id value = [object2 method]; // Error
This error is correct. If this is the error, it is solvable. But first we have to clarify that.
BTW: This has nothing to do with better OOP. First of all this is a problem of class based programming languages, not of object orientated programming languages. Second I cannot see that this set-up will break a rule of class based or object orientated programming, esp. it fulfills Liskov's rule.
http://en.wikipedia.org/wiki/Liskov_substitution_principle
One thing you could do is declare a LineChartModel variable and not property and #synthesize dataModel to that ivar:
#interface LineChartViewController: ChartViewController {
LineChartModel *_lineChartModel;
}
-(void)drawLine;
#end
#implementation LineChartViewController
synthesize dataModel = _lineChartModel;
....
So from the outside it looks like you have a ChartModel, but inside the class you have LineChartModel. You will have to apply changes to _lineCharModel directly inside your class.
HOWEVER this is NOT my definition of better OOP!!! There's clearly a fault with the design if you need to cast your variables in subclass.
Another option I discovered after pasting this code into the editor is just use self.variable (which by the way, you should've already been doing).
#interface ChartModel : NSObject
- (BOOL)chartModelhasData;
#end
#interface LineChartModel : ChartModel
- (UIColor *)getLineColor;
#end
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
- (void)layoutChartCanvas;
#end
#implementation ChartViewController
- (void)layoutChartCanvas {
if ([self.dataModel chartModelhasData]) {
self.view.hidden = NO;
}
}
#end
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
- (void)drawLine;
#end
#implementation LineChartViewController
- (void)drawLine {
UIColor *color = [self.dataModel getLineColor];
[self drawLine];
}
#end

Calling instance method to access IBOutlet instance from another class

I've been searching through a lot of forum but it's still driving me crazy.
i don't understand how to access IBOutlet instance for example of class 'A' from class 'B' which is attempting to change the value let's say the instance type is IBOutlet NSTextLabel *progressStatus with stringValue "Capturing" want to change to "Recognizing". It can be called only from the instance method of classA it self, but not from another class.
#interface classA : NSView
{
NSTextField *progressStatus;
}
#property (assign) IBOutlet NSTextField *progressStatus;
-(void)recognizeStatus;
#end
#import "classA.h"
#implementation classA
#synthesize progressStatus;
-(void)awakeFromNib
{
[self recognizeStatus]; //successfully change the value inside progressStatus
}
-(void)recognizeStatus
{
[progressStatus setStringValue:#"Recognizing"];
NSLog(#"Progress Status : %#",progressStatus.stringValue);
}
- (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection*)connection
{
//code to convert videoFrame to IplImage type named frameImage
BOOL faceDetected = [classB faceDetection:frameImage];
}
#end
#import "classA.h"
#interface classB : NSObject
{
}
+(BOOL)faceDetection:(IplImage*)source;
#end
#import "classA.h"
#import "classB.h"
#implementation classB
+(BOOL)faceDetection:(IplImage*)source
{
classA *status = [[classA alloc] init];
[status recognizeStatus]; //no changes with the value inside progressStatus
//return bool type
}
#end
In classB's class method faceDetection:you initialize an instance of classA which derives from NSView, but you do not add the instance to the view hierarchy. Further, in initializing the instance of classA you aren't loading the view from a nib, so the instance of classA status likely has a nil progressStatus.
I suspect you may be trying to access an instance of classA that was already initialized and added to the view hierarchy elsewhere; but initializing another instance won't provide you with a reference to the original instance.

No access to global instance (build by factory) on iOS

this is a follow-up question to my last one here: iOS: Initialise object at start of application for all controllers to use .
I have set my application up as follows (ignore the DB Prefix):
DBFactoryClass // Built a DataManaging Object for later use in the app
DBDataModel // Is created by the factory, holds all data & access methods
DBViewControllerA // Will show some of the data that DBDataModel holds
moreViewControllers that will need access to the same DBDataModel Object
i will go step by step through the application, and then post the problem in the end
AppDelegate.h
#import "DBFactoryClass.h"
AppDelegate.m
- (BOOL)...didFinishLaunching...
{
DBFactoryClass *FACTORY = [[DBFactoryClass alloc ]init ];
return YES;
}
DBFactoryClass.h
#import <Foundation/Foundation.h>
#import "DBDataModel.h"
#interface DBFactoryClass : NSObject
#property (strong) DBDataModel *DATAMODEL;
#end
DBFactoryClass.m
#import "DBFactoryClass.h"
#implementation DBFactoryClass
#synthesize DATAMODEL;
-(id)init{
self = [super init];
[self setDATAMODEL:[[DBDataModel alloc]init ]];
return self;
}
#end
ViewControllerA.h
#import <UIKit/UIKit.h>
#import "DBDataModel.h"
#class DBDataModel;
#interface todayViewController : UIViewController
#property (strong)DBDataModel *DATAMODEL;
#property (weak, nonatomic) IBOutlet UILabel *testLabel;
#end
ViewControllerA.m
#import "todayViewController.h"
#implementation todayViewController
#synthesize testLabel;
#synthesize DATAMODEL;
- (void)viewDidLoad
{
todaySpentLabel.text = [[DATAMODEL test]stringValue]; // read testdata
}
#end
DBDataModel.h
#import <Foundation/Foundation.h>
#interface DBDataModel : NSObject
#property (nonatomic, retain) NSNumber* test;
#end
DBDataModel.m
#import "DBDataModel.h"
#implementation DBDataModel
#synthesize test;
-(id)init{
test = [[NSNumber alloc]initWithInt:4]; // only a testvalue
return self;
}
#end
the app builds fine, and starts up but the label stays blank. so either the object does not exist (but i guess this would result in an error message), or something else is wrong with my setup. any thoughts?
Two notes:
Your have a shotgun approach to asking questions: everytime you hit a stumbling block, you ask a question and if the answer does not work immediately, you ask another one. You have to spend some energy in between the questions debugging and poking into the code on your own, otherwise you will depend on the external help forever.
Use the common coding style please. CAPS are reserved for macros.
Now to the code:
- (BOOL) …didFinishLaunching…
{
DBFactoryClass *factory = [[DBFactoryClass alloc] init];
return YES;
}
This simply creates an instance of the DBFactoryClass and then throws it away. In other words, it’s essentially a no-op. Judging by the comments in the previous answer you create the controllers using the Storyboard feature. How are they supposed to receive the reference to the data model? The reference isn’t going to show up by magic, you have to assign it somewhere.
I’m not familiar with the Storyboard feature. The way I would do it is to create the view controllers using separate XIB files, then you can create the controller instances in the Factory class and pass them the needed reference to the model. In the end the application delegate would create the factory, ask it to assemble the main controller and then set it as the root view controller for the window. Just like in my sample project. It’s possible that there’s a way to make it work with storyboards, but as I said, I am not familiar with them.

EXC_BAD_ACCESS when synthesizing a 'global' object

this is a follow-up question to my last one here: iOS: Initialise object at start of application for all controllers to use .
I have set my application up as follows (ignore the DB Prefix):
DBFactoryClass // Built a DataManaging Object for later use in the app
DBDataModel // Is created by the factory, holds all data & access methods
DBViewControllerA // Will show some of the data that DBDataModel holds
moreViewControllers that will need access to the same DBDataModel Object
i will go step by step through the application, and will then in the end post the error message i get when building.
AppDelegate.h
#import "DBFactoryClass.h"
AppDelegate.m
- (BOOL)...didFinishLaunching...
{
DBFactoryClass *FACTORY = [[DBFactoryClass alloc ]init ];
return YES;
}
DBFactoryClass.h
#import <Foundation/Foundation.h>
#import "DBDataModel.h"
#interface DBFactoryClass : NSObject
#property (strong) DBDataModel *DATAMODEL;
#end
DBFactoryClass.m
#import "DBFactoryClass.h"
#implementation DBFactoryClass
#synthesize DATAMODEL;
-(id)init{
self = [super init];
[self setDATAMODEL:[[DBDataModel alloc]init ]];
return self;
}
#end
ViewControllerA.h
#import <UIKit/UIKit.h>
#import "DBDataModel.h"
#class DBDataModel;
#interface todayViewController : UIViewController
#property (strong)DBDataModel *DATAMODEL;
#property (weak, nonatomic) IBOutlet UILabel *testLabel;
#end
ViewControllerA.m
#import "todayViewController.h"
#implementation todayViewController
#synthesize testLabel;
#synthesize DATAMODEL;
- (void)viewDidLoad
{
todaySpentLabel.text = [[DATAMODEL test]stringValue];
}
#end
DBDataModel.h
#import <Foundation/Foundation.h>
#interface DBDataModel : NSObject
#property (nonatomic, retain) NSNumber* test;
#end
DBDataModel.m
#import "DBDataModel.h"
#implementation DBDataModel
#synthesize test;
-(id)init{
test = [[NSNumber alloc]initWithInt:4];
return self;
}
#end
when i build it, i get the following error: EXC_BAD_ACCESS in this line:
#synthesize DATAMODEL;
of DBFactoryClass.m
What #synthesize does is to automatically generate implementations of the accessors for a property. EXC_BAD_ACCESS there means that you're accessing garbage when one of the accessors is executed.
That's probably happening here:
[self setDATAMODEL:[[DBDataModel alloc]init ]];
Make sure that DBDataModel's implementation of init actually returns a legitimate object.
As far as I can tell, your DBFactoryClass class is never stored anywhere, and therefore released right after the allocation if you use ARC (Since you use the strong keyword I assumed you do).
- (BOOL)...didFinishLaunching... {
DBFactoryClass *FACTORY = [[DBFactoryClass alloc ]init ];
// If you use ARC this might be released right afterwards
return YES;
}
If you want the factory to be a singleton, use something like this
+ (id)sharedInstance {
static dispatch_once_t once;
static MyFoo *instance;
dispatch_once(&once, ^{
instance = [[self alloc] init];
});
return instance;
}

How do I access my array from my table data source?

I am starting my first Cocoa Project. And I have a serious (for me) but maybe easy problem (for you) to solve and I need some direction where to start.
The short description: I have built a class "PortConnection.h" who writes all ports found by an external class (AMSerial.h) into an array when the function -listPorts is called. Here is the code for the PortConnection.h
#import <Cocoa/Cocoa.h>
#import "AMSerialPortList.h"
#import "AMSerialPortAdditions.h"
#import "AMSerialPort.h"
#interface PortConnection : NSObject {
#private
AMSerialPort *port;
NSMutableArray *portArray;
}
- (void)listDevices;
#property (nonatomic, retain) NSMutableArray *portArray;
#property (nonatomic, retain) AMSerialPort *port;
#end
and following the PortConnection.m
#import "PortConnection.h"
#import "AMSerialPortList.h"
#import "AMSerialPortAdditions.h"
#import "AMSerialPort.h"
#implementation PortConnection
#synthesize portArray;
#synthesize port;
- (void)listDevices
{
// get an port enumerator
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
while ((aPort = [enumerator nextObject]))
{
// Add Devices to Array
[portArray addObject:[aPort bsdPath]];
}
}
So far it is perfectly working.
Now my questions
I have implemented an tableView in the GUI for display the results of the method called above. This file is called "PortTableViewController.h" and is the datasource for my TableView.
Here is the .h file
#import <Foundation/Foundation.h>
#import "PortConnection.h"
#interface PortTableViewController : NSObject <NSTableViewDataSource> {
#private
IBOutlet NSTableView *portTableView;
}
#property (assign) IBOutlet NSTableView *portTableView;
#end
and here is the .m file:
#import "PortTableViewController.h"
#import "PortConnection.h"
#implementation PortTableViewController
#synthesize portTableView;
#pragma mark -
#pragma mark TableView Delegates
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
PortConnection *portConnection = [[PortConnection alloc] init];
[portConnection listDevices];
return [portConnection.portArray count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
??????? I DO NOT HAVE A CLUE HOW TO ACCESS THE ARRAY IN PORTCONNECTION.M CORRECTLY
}
#end
QUESTIONS:
1) When having a look on the TableViewDataSourceDelegates how do I access the filled portArray in the PortConnection.m class correctly. It doesn't seem to work the way I do it.
2) Do I have to create an Object from Portconnection.h every time I want to retrieve Data from it in a tableviewdelegate method?
I am really thankful for every kind of help! I want to learn something.. and I really appreciate your support! Thanks.. for question in order to help me, don't hesitate. I really appreciate it....
Thanks
Sebastian
A simple fix is to have your table view controller declare an instance variable that holds a PortConnection instance. This instance is created and sent -listDevices in -init, it is used by all methods in your table view controller (which means that all methods refer to the same PortConnection instance), and released in -dealloc.
For example:
PortTableViewController.h
#import <Foundation/Foundation.h>
#import "PortConnection.h"
#interface PortTableViewController : NSObject <NSTableViewDataSource> {
#private
IBOutlet NSTableView *portTableView;
PortConnection *portConnection;
}
#property (assign) IBOutlet NSTableView *portTableView;
#end
PortTableViewController.m
#import "PortTableViewController.h"
#import "PortConnection.h"
#implementation PortTableViewController
#synthesize portTableView;
#pragma mark -
#pragma mark TableView Delegates
- (id)init {
self = [super init];
if (self) {
portConnection = [[PortConnection alloc] init];
[portConnection listDevices];
}
return self;
}
- (void)dealloc {
[portConnection release];
[super dealloc];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return [portConnection.portArray count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
return [portConnection.portArray objectAtIndex:row];
// or whatever behaviour provides an object value for the column/row
}
#end
If you don't want to create a field in every delegate you could create a static variable in PortConnection which holds the array. Initially the array is nil and on the first call to get the ports you create the list if needed.
In the implementation file:
static NSMutableArray *portArray;
+ (NSArray) listPorts {
if(portArray != nil)
return (NSArray *)portArray;
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
portArray = [[NSMutableArray alloc] init];
while ((aPort = [enumerator nextObject])) {
// Add Devices to Array
[portArray addObject:[aPort bsdPath]];
}
}
This of course depends on how often the portArray will change, if it's often I would probably just generate it every time.
You could also do a getPortArray which calls generatePortArray if portArray is nil
You should only need a single PortConnection instance, but your table view controller will somehow need to know about it. It could be that the PortTableViewController creates and owns the PortConnection object, or it could be that some other object, like the app delegate or another controller creates it.
In the former case, it's trivial... the PortTableViewController creates the PortConnection instance, and therefore it has a reference to it and can access its portArray property at well.
In the latter case, things aren't much more complicated: the object that creates the PortController should give the PortTableViewController a pointer to the PortController. The PortTableViewController should then retain the PortController and stash the pointer in an instance variable so that it can access the portArray property as needed.