iOS: Methods of ViewController subclass not visible in other classes - objective-c

I'm currently trying to develop an iPhone application.
Most things are working as I would expect and prefer.
Right now the issue I'm having is that when adding methods to one of my ViewControllers the methods are not visible from other parts of my applications.
When I add the same methods, with the same signature to my other view controllers they will be visible.
I've googled, browsed stackoverflow, reread, copy/pasted, and prayed to the spaghetti monster for divine insight, but to no avail.
There must be some minor detail, that I in my folly am overlooking. I hope you will be able to help me!
InfoPageViewController.h
#import <UIKit/UIKit.h>
#import "DB.h"
#interface InfoPageViewController : UIViewController
{
IBOutlet UIWebView* wv;
DB* db;
}
#property (retain, nonatomic) IBOutlet UIWebView* wv;
-(void) reloadInfoPage;
#end
InfoPageViewController.m
#import "InfoPageViewController.h"
#interface InfoPageViewController ()
#end
#implementation InfoPageViewController
#synthesize wv;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Information", #"Information");
self.tabBarItem.image = [UIImage imageNamed:#"TabIcon-Settings"];
db = [[DB alloc] init];
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
DBInfoPage* dbip = [db getInfopage];
[wv loadHTMLString:dbip.html baseURL:nil];
//NSLog(#"word%#", dbip.html);
[self reloadInfoPage];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(void)reloadInfoPage
{
DBInfoPage* dbip = [db getInfopage];
[wv loadHTMLString:dbip.html baseURL:nil];
NSLog(#"reloading infopage%#", #"");
}
#end
infoviewtest.h
#import <UIKit/UIKit.h>
#import "InfoPageViewController.h"
#interface infoviewtest : NSObject
#end
infoviewtest.m
#import "infoviewtest.h"
#implementation infoviewtest
-(void)test
{
InfoPageViewController* ivc = [[InfoPageViewController alloc] init];
[ivc reloadInfoPage];
}
#end
This yields an error of "No visible #interface for 'InfoPageViewController' declares the selector 'reloadInfoPage'.
I have also tried to use autocomplete to show me the available methods of the 'InfoPageViewController', this yields a list not containing 'reloadInfoPage', similarly the instance variable 'wv' is not visible from outside of the scope of the class.
I have tried closing and reopening xcode, as well as restart the computer.
I have also tried to 'clean' the project.
Any help will be greatly appreciated by the parts of my hair, not yet pulled in frustration.
If I have been lacking in providing information, please request and I'll do my best to respond.
Johan Abildskov

Actually your code look correct - all you have to do is probably clean the project and possibly restart XCode.
But you could optimize the linking/compiling by making some modifications like:
infoviewtest.h
#import <UIKit/UIKit.h>
//#class InfoPageViewController; //add this if you'll be adding ivar or property of this type
#interface infoviewtest : NSObject
#end
infoviewtest.m
#import "infoviewtest.h"
#import "InfoPageViewController.h" //here's the place to import other headers
#implementation infoviewtest
-(void)test
{
InfoPageViewController* ivc = [[InfoPageViewController alloc] init];
[ivc reloadInfoPage];
}
#end
EDIT: Make sure that your implementation of DB class is correct. XCode error might lead you to wrong assumptions. Again: the code you've posted seems correct, just not optimized for compilation.

I've noticed an odd issue with test targets, when you don't include a class in the test target it still compiles, this could could be whats happening here so:
Have you included InfoPageViewController in your test target?

Your code is perfect it seems. Even DB.h having the same method name, it will not affect the declaration of the same method in your controller class. Its quite strange error. Probably your XCode might get corrupted. Try to update Xcode and try the same code. If you are updating Xcode, update the iTunes too. This may fix your problem I think.

Just in case anyone else is suffering from this weird problem as I just did - the cause in my case was due to my ending up with 2 copies of the same class .m & .h files in different directories of my project after my Xcode newbie fumbling attempt to move them from one disk directory to another while keeping them in the same Xcode group. Xcode ended up allowing me to edit one set of files but used the other for compilation so the method that I added really didn't exist as far as the compiler was concerned, hence the above error message.

Related

Creating an Instance of Class in IOS 6 [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have a problem that I hope you can help me with. I have an app with several storyboard views, each with a separate viewcontroller. I want to be able to use my own class in all views in the storyboard. This will make the code in each viewcontroller much cleaner and the whole app easier to debug etc. The class will contain variables and methods.
The overall aim for me is to collect data from the user via buttons and then store these in a database. It will be possible to view and amend data, as well as generating statistics.
As most variables and methods will be used in different views and at different times, I would like to separate all these in separate files.
I can also tell you that this is not a lazy short-cut attempt from me, I have surfed the internet for many, many hours reading hundreds of posts etc and I am still nowhere nearer a solution.
Any input is very much appreciated!
Thank you for taking your time to read this...
SomeClass.h has the following code:
#import <Foundation/Foundation.h>
#interface SomeClass : NSObject
{
NSString *dataOne;
NSString *dataTwo;
NSString *dataThree;
}
- (void) SetDataOne: (NSString*) dataOneReceived;
- (void) SetDataTwo: (NSString*) dataTwoReceived;
- (void) SetDataThree: (NSString*) dataThreeReceived;
- (void) saveSomeData;
#end
SomeClass.m has the following code:
#import "SomeClass.h"
#implementation SomeClass
- (void) SetDataOne: (NSString*) dataOneReceived {
dataOne = dataOneReceived;
}
- (void) SetDataTwo: (NSString*) dataTwoReceived {
dataTwo = dataTwoReceived;
}
- (void) SetDataThree: (NSString*) dataThreeReceived {
dataThree = dataThreeReceived;
}
- (void) saveSomeData {
// Here I do stuff with dataOne etc…
}
#end
SomeView.h has the following code:
#import <UIKit/UIKit.h>
#import "HeadViewController.h"
#import "SomeClass.h"
#interface SomeView : UIViewController
// contains stuff not needed to show here
- (IBAction)Done:(id)sender;
#end
SomeView.m has the following code:
#import "SomeView.h"
#import "SomeClass.h"
#interface SomeView ()
#end
#implementation SomeView
- (void)viewDidLoad
{
[super viewDidLoad];
SomeClass *someClassObject = [[SomeClass alloc] init];
// Do any additional setup after loading the view.
}
// Other standard methods omitted
- (IBAction)Done:(id)sender {
[someClassObject SetDataOne: #”whatever text”];
[someClassObject SetDataTwo: #”whatever text”];
[someClassObject SetDataThree: #”whatever text”];
[someClassObject SaveSomeData];
Error Msg for all the above: ”Use of Undeclared Identifier ’someClassObject’
}
#end
Comment: You can see the error message I get at the end of the code above. I have no clue what I am doing wrong. I have looked at a lot of examples on how to create and call classes, but cannot seem anything that solves my problem. Also, I see that some of the put the ”SomeClass *someClassObject = [[SomeClass alloc] init];” in the ”main.m file”. If I understand correctly, that file is the first one to load when app starts. If so, then I cannot place it there as I will have to create instances of my class in several different views and other times than when the app starts. That is why I have placed it in the viewDidLoad-method.
A couple of thoughts:
You've made someClassObject a local variable of the viewDidLoad method. Looks like you meant to make it a class instance variable (or, better, a private class property, which will have the instance variable synthesized for you). Thus:
#interface SomeView ()
#property (nonatomic, strong) SomeClass *someClassObject;
#end
#implementation SomeView
- (void)viewDidLoad
{
[super viewDidLoad];
self.someClassObject = [[SomeClass alloc] init];
}
- (IBAction)done:(id)sender {
[self.someClassObject setDataOne: #"whatever text"];
[self.someClassObject setDataTwo: #"whatever text"];
[self.someClassObject setDataThree: #"whatever text"];
[self.someClassObject saveSomeData];
// should resolve the Error Msg for all the above: ”Use of Undeclared Identifier ’someClassObject’
}
BTW, as a matter of convention, your method names should start with a lowercase letter (e.g. setDataOne not SetDataOne, done rather than Done, etc.), as illustrated above.
If you're going to write your own setters, setDataOne, setDataTwo, etc., you might as well remove those three instance variables, remove your three setData___ methods, and replace the three instance variables with class properties (and let the compiler synthesize not only the instance variables, but the setters, too).
someClassObject is set inside viewDidLoad and because it is not stored anywhere inside the view will be deleted at the end of that very same method
You should add your object inside each header file's interface section like this:
SomeView.h
#import <UIKit/UIKit.h>
#import "HeadViewController.h"
#import "SomeClass.h"
#interface SomeView : UIViewController
{
SomeClass *someClassObject;
}
// contains stuff not needed to show here
- (IBAction)Done:(id)sender;
#end
Then when you instantiate someClassObject inside ViewDidLoad it will persist throughout that view.

Objective-C methods not running

I'm messing around with using objects to launch background threads, however when I call an objects method to call the method that will spawn a background thread, nothing happens. I'm a bit puzzled as to why, and it looks like the -init function isn't even being called. Anyways, here's what I have:
ViewController.h
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
#property(nonatomic, strong) Thread* threadedObject;
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "Threader.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_threadedObject = [[Threader alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender {
NSLog(#"Clicked.");
[_threadedObject RunInBackground];
}
#end
Threader.h
#import <Foundation/Foundation.h>
#interface Threader : NSObject
#property(nonatomic) bool IsFinishedRunning;
#property(nonatomic) bool IsThreading;
//Constructor and Destructor
-(id)init;
-(void)dealloc;
-(void)RunInBackground;
-(void)WaitForTenSeconds;
#end
Threader.m
#import "Threader.h"
#implementation Threader
//constructor
-(id)init{
[super init];
if(self != nil)
{
_IsFinishedRunning = NO;
_IsThreading = NO;
}
return self;
}
//destructor
-(void)dealloc{
[super dealloc];
}
//Runs a thread in the background
-(void)RunInBackground{
NSLog(#"Initiating thread...");
[self performSelectorInBackground:#selector(WaitForTenSeconds) withObject:nil];
}
//Waits for 10 seconds, then sets IsFinishedRunning to YES
-(void)WaitForTenSeconds{
NSLog(#"Starting to run in the background.");
_IsThreading = YES;
sleep(10);
_IsFinishedRunning = YES;
NSLog(#"Finished running in the background.");
}
#end
When I run the program, this is my output(I clicked the button a few times)
2013-05-17 15:30:57.267 ThreadedObjects Clicked.
2013-05-17 15:30:59.003 ThreadedObjects Clicked.
2013-05-17 15:30:59.259 ThreadedObjects Clicked.
2013-05-17 15:30:59.443 ThreadedObjects Clicked.
2013-05-17 15:30:59.675 ThreadedObjects Clicked.
I should be getting messages telling me that the Threader object was created, and that it is preparing to launch a background thread, that the thread has been spawned and then after 10 seconds, that the thread is done running.
So, where's my glaring obvious error?
init isn't a constructor, it's for setup after construction. You need the class object to create an instance before you can send init, and, most importantly, you need to assign the results to your variable.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
threadedObject = [[Threader alloc] init];
}
You can't send alloc to an object that's not a class; instances don't respond to it. The only reason that this isn't crashing is that globals are initialized to 0/NULL/nil, and [nil someMessage] does nothing.
Not assigning the results to your variable is the same as:
int x = 0;
x + 10;
There's no change to x's value.
Additionally, you don't seem to have an ivar there, just a global variable. Ivars need to go into a curly-brace block at the head of the #implementation:
#implementation Threader
{
Threader * threadedObject;
}
// etc...
You never alloc the object.............
Also, this is curious:
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
Threader* threadedObject;
Where exactly did you declare the threadedObject? Like above? Use an iVar
or, better, a property for it!
A couple of reactions:
Show us where your definition and alloc/init of threadedObject.
I'm not sure what business problem you're trying to solve, but this smells like the precursor of some custom NSOperation solution. Operation queues are ideally suited for these sorts of implementations.
I'd be inclined to subclass NSOperation when trying to do something like this. See the custom NSOperation object in the Concurrency Programming Guide.
I'd suggest using camelCase for your method and variable names.
If you say with this, I'd steer you away from the "thread" name, as it might imply that you're doing something with NSThread, which you're not.

Cocoa Programming, setting the delegate

I'm moving on from iOS to Cocoa and trying to muddle through my first few programs. I thought it would be simple to add an NSComboBox to my form, well that part was. I added <NSComboBoxDelegate, NSComboBoxDataSource> to my interface, two data callbacks, and the notifier:
#interface spcAppDelegate : NSObject <NSApplicationDelegate,
NSComboBoxDelegate, NSComboBoxDataSource>
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index;
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox;
- (void)comboBoxSelectionDidChange:(NSNotification *)notification;
#end
I control dragged the combobox to the app delegate (which is the only class in my simple default app) and wired up the delegate and data source but none of those events fire. I thought app delegate was correct but since it didn't fire, I also tried "file owner" and "application". I didn't think those would work and they didn't.
Whats the right way to wire up the delegate/data source for an NSComboBox in a Cocoa app?
Thanks!
Provided you've actually implemented those methods in your spcAppDelegate.m file, you may want to double-check that Uses Data Source is checked for the NSComboBox in the nib file in Interface Builder:
Note that it wasn't set by default in a quick test project I created. Running without that checkbox set should log the following to console when you launch the app:
NSComboBox[2236:403] *** -[NSComboBox setDataSource:] should not be called when
usesDataSource is set to NO
NSComboBox[2236:403] *** -[NSComboBoxCell setDataSource:] should not be called
when usesDataSource is set to NO
While the NSComboBox Class Reference is somewhat helpful, when I was first learning, I found that if there were companion guides linked to for a class, those were much more helpful in understanding how one should use the class in practice. If you look at the top of the NSComboBox class reference at the Companion Guide, you'll see Combo Box Programming Topics.
To set up a combo box that uses a data source, you could use something like the following:
spcAppDelegate.h:
#import <Cocoa/Cocoa.h>
#interface spcAppDelegate : NSObject <NSApplicationDelegate,
NSComboBoxDelegate, NSComboBoxDataSource> {
IBOutlet NSWindow *window;
IBOutlet NSComboBox *comboBox;
NSMutableArray *comboBoxItems;
}
#property (assign) IBOutlet NSWindow *window;
#end
spcAppDelegate.m:
#import "spcAppDelegate.h"
#implementation spcAppDelegate
#synthesize window;
- (id)init {
if ((self = [super init])) {
comboBoxItems = [[NSMutableArray alloc] initWithArray:
[#"Cocoa Programming setting the delegate"
componentsSeparatedByString:#" "]];
}
return self;
}
- (void)dealloc {
[comboBoxItems release];
[super dealloc];
}
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox {
return [comboBoxItems count];
}
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index {
if (aComboBox == comboBox) {
return [comboBoxItems objectAtIndex:index];
}
return nil;
}
- (void)comboBoxSelectionDidChange:(NSNotification *)notification {
NSLog(#"[%# %#] value == %#", NSStringFromClass([self class]),
NSStringFromSelector(_cmd), [comboBoxItems objectAtIndex:
[(NSComboBox *)[notification object] indexOfSelectedItem]]);
}
#end
Sample Project: http://github.com/NSGod/NSComboBox.
I was having a similar situation yesterday until I remembered to hook up the File Owner data source to the IBOutlet in IB:

Strange ARC issue not releasing ivar in UIView subclass [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why is object not dealloc'ed when using ARC + NSZombieEnabled
I've got a very strange issue I'm seeing at the moment in a project. Put simply I have ViewA which owns ViewB (strong property). ViewA creates its ViewB in its initialiser. Both objects are subclasses of UIView.
I have overridden dealloc in both and put a log line and a break point to see if they get hit. It seems that ViewA's dealloc is being hit but not ViewB's. However if I put in a self.viewB = nil in the dealloc of ViewA then it is hit.
So basically it's something like this:
#interface ViewA : UIView
#property (nonatomic, strong) ViewB *viewB;
#end
#implementation ViewA
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.viewB = [[ViewB alloc] initWithFrame:self.bounds];
[self addSubview:self.viewB];
}
return self;
}
- (void)dealloc {
//self.viewB = nil; ///< Toggling this commented/uncommented changes if ViewB's dealloc gets called.
NSLog(#"ViewA dealloc");
}
#end
What I can't understand is why nil-ing viewB out makes a difference. If something else is holding onto viewB then it should make absolutely no difference if I nil it out or not here. And it shouldn't make a difference to the number of releases that ARC adds in either.
I can't seem to reproduce it in a minimal test case as yet, but I'm working on it. And I can't post the actual code I'm seeing this in unfortunately. I don't see that being an issue though because it's more the point that nil-ing it out shouldn't make a difference that I am confused by.
Can anyone see anything I am overlooking or give advice about where to look for debugging this problem?
Update:
I've found the problem. It appears that it's only a problem when NSZombieEnabled is set to YES. Well that is entirely mad and has to be a bug surely. Zombies should not affect how this works as far as I know. The objects should still go through the dealloc method. And what's more, it's just mad that it works if I nil out viewB in ViewA's dealloc.
I've found that this appears to be a bug in the iOS implementation of zombies. Consider the following code:
#import <Foundation/Foundation.h>
#interface ClassB : NSObject
#end
#implementation ClassB
- (id)init {
if ((self = [super init])) {
}
return self;
}
- (void)dealloc {
NSLog(#"ClassB dealloc");
}
#end
#interface ClassA : NSObject
#property (nonatomic, strong) ClassB *b;
#end
#implementation ClassA
#synthesize b;
- (id)init {
if ((self = [super init])) {
b = [[ClassB alloc] init];
}
return self;
}
- (void)dealloc {
NSLog(#"ClassA dealloc");
}
#end
int main() {
ClassA *a = [[ClassA alloc] init];
return 0;
}
That should output:
ClassA dealloc
ClassB dealloc
But with NSZombieEnabled set to YES, it outputs:
ClassA dealloc
As far as I can tell, this is a bug. It seems to only happen with iOS (both simulator and device) and does not happen when built and run for Mac OS X. I've filed a radar with Apple.
Edit: It turns out this has already been answered here - Why is object not dealloc'ed when using ARC + NSZombieEnabled . Managed to find it after I found out what the real problem was. It's nothing to do with ARC by the way.

Property/Synthesize errors

Sorry to keep asking basic questions here but I don't know where else to go. Wrote some code with a slider, textfield and buttons for incrementing the slider to demonstrate key value coding. Everything worked find. The next step was to use 'property' and 'synthesize' in place of the accessor and setter methods;
#import <Foundation/Foundation.h>
#interface KVCController : NSObject {
int fido;
}
#property(readwrite, assign) int fido;
#end
~~~~~
#implementation KVCController
#synthesize fido;
- (id)init{
self = [super init];
if (self) {
// Initialization code here.
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
}
return self;
}
~~~~~~~
#end
I get an incomplete implementation error on #implementation KVCController. If I put the get and set methods for 'fido' in it clears up.
The second error occurs with #synthesize fido;. It says property must be declared in the implementation. Everything is copied correctly out of the book and near as I can tell, it looks just like all the other uses of property and synthesize I have looked at. Anyone have any ideas on what I am missing or doing wrong?
Xcode 4.1 automatically creates a delegate class which I usually ignore if I am not working on delegates. I created my own class for the KVC exercise and just added the property/synthesize declarations to it with appropriate modifications and got the errors. I just put the property/synthesize declarations into the delegate class, moved my IBAction code to the appropriate places, redid the bindings, and erased the class I created and everything worked fine. Do property/synthesize declarations need to be treated like delegate material?
incomplete implementation means you have a -(void)something that may be defined in your header that you are not using in your #implementation. Make sure that you do not have any unused methods listed in your header. if you do, either remove them from the header, or create the method in your implementation.
- (void) dosomething
{
/* blank for now */
}
if you have -(void)dosomething in your implementation, define it in your header.
#import <Foundation/Foundation.h>
#interface KVCController : NSObject {
int fido;
}
#property(readwrite, assign) int fido;
- (void) dosomething;
#end