Looked for an answer for this question, but I haven't found a suitable one yet. I'm hoping you guys (and gals) can help me out! (This is for an iPhone app)
Alright, I have a Mutliview application. Each view has it's own class, and everything is happy. However, the different classes sometimes call the same method. Up until now, I have simply wrote that Method twice, in both of the class files.
This is what I want to do though:
I want to make a new class, in It's own file, that has all the "Common" Methods. Then, whenever another class needs to call the Method, I simply call it from the other file. This way, when I want to change the Method, I only need to change it in one place, and not all the places...
I'm not sure how I'd do this, which is why I'm asking for help. I'm a little rusty and new for Objective-C, so pretty examples will help me a lot. Allow me to give you one.
File: ViewController1.m
#implementation ViewController1
//Do Some awesome stuff....
CALL "CommonMethod" HERE
#end
File: ViewController2.m
#implementation ViewController2
//Do Some awesome stuff....
CALL "CommonMethod" HERE
#end
File: CommonClass
#implementation commonClass
- (void)CommonMethod:(id)sender
{
//So some awesome generic stuff...
}
#end
I feel like I need to #import the other file, make an Object from the class and call the Method from the Object... How do I do that?
Thanks again!
Option 1:
#implementation commonClass
+ (void)CommonMethod:(id)sender /* note the + sign */
{
//So some awesome generic stuff...
}
#end
#implementation ViewController2
- (void)do_something... {
[commonClass CommonMethod];
}
#end
Option 2:
#implementation commonClass
- (void)CommonMethod:(id)sender
{
//So some awesome generic stuff...
}
#end
#implementation ViewController2
- (void)do_something... {
commonClass *c=[[commonClass alloc] init];
[c CommonMethod];
[c release];
}
#end
Option 3: use inheritance (see Mr. Totland's description in this thread)
#implementation commonClass
- (void)CommonMethod:(id)sender
{
//So some awesome generic stuff...
}
#end
/* in your .h file */
#interface ViewController2: commonClass
#end
naturally you always need to #import commonClass.h in your view controllers..
There are some answers here telling you to create a common "parent" class. However I think that you can do a lot better. Create a category for UIViewController instead. You don't know all of the internals of what is going on with UIViewController so I don't think it is worth creating your own View Controller hierarchy off of. In fact it could be dangerous. I ran into a number of problems when I tried to create a "base" UITableViewController and then create classes that inherit from that. I avoided these problems by using categories instead.
Your #1 priority shouldn't be inheriting things for no good reason, it should be getting an app into the app store that people will want to download.
It sounds to me like the common code doesn't need to be in a class at all. Is there a reason you can't just use a C-style function for what you want to do?
You could put the common code in a class and then make your other two classes subclasses of that one; this method also avoids the code duplication.
Another option might be to write a class method instead of instance methods for this common code. I think most people feel that singletons are best avoided as a design choice.
It would be easier to give a good answer if we knew more about what you were really trying to accomplish.
What you want to do is to make the two controllers share a common superclass:
UIViewController : MyAwesomeViewController : ViewController1
: ViewController2
commonMethod: would then reside in MyAwesomeViewController. Also, don't start method names with capital letters. :)
To elaborate:
+#interface MyAwesomeController : UIViewController {
-#interface ViewController1 : UIViewController { // and ditto for ViewController2
+#interface ViewController1 : MyAwesomeController {
Bear in mind that Objective-C is just a superset of C, and that whilst #include directives are mostly used for header files, there's nothing stopping you using a #include to embed the contents of one implementation inside another implementation. If the code is truly identical, you can easily just stick it in its own file, and #include it in the .m file.
Having said that, perhaps it would be better to use this technique in conjunction with categories, especially if the same implementation has similar behaviours.
Pass in a reference to your commonClass when you alloc and init your views...
CommonClass *cc = [[CommonClass alloc] init];
ViewController1 *vc1 = [[ViewController1 alloc] ... initWith:cc];
ViewController2 *vc2 = [[ViewController2 alloc] ... initWith:cc];
but making a classic c include might suffice.
As an iPhone neophyte with a Java background and little C, I had a similar problem wishing to refer to a method in both the RootController and a ViewController. It seemed to me that the proper place for the method was the AppDelegate class, an instance of which one obtains in other classes by:
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
then if the method is "doSomething" one accesses it by:
[delegate doSomething];
But perhaps this is too obvious or not what was required.
Another method that you can use
#interface ServerManager : NSObject
+(ServerManager *)getInstance;
#implementation ServerManager
+(ServerManager *)getInstance
{
static ServerManager *objServerManager = nil;
if(objServerManager==NULL){
objServerManager=[[self alloc] init];
}
// Return the servermanager object.
return objServerManager;
}
Call Whether you want to use
ServerManager *SMObject = [ServerManager getInstance];
Don't forget to import servermanager.h file.
Related
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.
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.
I have an custom UIVIewController that is the base class for other controllers and has an instance of a custom UIView variable that is accessed by inherited the classes.
BaseViewController.h
#interface BaseViewController : UIViewController {
UIView *_vwHeader;
}
#end
BaseViewController.m
#import "BaseViewController.h"
#implementation BaseViewController
-(void)loadView {
[super loadView];
_vwHeader = [[UIView alloc] init];
}
#end
CustomViewController.h
#import "BaseViewController.h"
#interface CustomViewController : BaseViewController
#end
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (void)loadView
{
[super loadView];
[_vwHeader setHidden:NO];
}
#end
The problem is that when I am running it on the simulator everything works perfectly fine, but when I change to the device I have an error on the [_vwHeader setHidden:NO]; line which says: '_vwHeader' undeclared (first use in this function)
I already tried to do:
Comment this line of code, but then it gives me an error in another class using a variable from the base class the same way (It only returns one error at a time), so it seems that it is not an specific error in the view or the controller class as the error occurs in other clases with different types, such as UIView and NSObject types
Change target compiler configuration, such as: architectures (all of them), base sdk (all above 4.0) didn't change anything
What seem to solve the problem, but not completely
Creating a property for _vwHeader and accessing it by self._vwHeader or super._vwHeader seems to work, but having to create a property just to access a variable does not make me confortable, specially because I would have to do it for all variables in the same situation inside my project.
changed C/C++ compiler version: using Apple LLVM Compiler 2.1 makes the compilation error goes away, but gives a bunch of other problems with other libraries being used in the project. So, it is not a definitive solution, but might be a clue of what the problem is.
EDIT:
I tried to create another variable that is not a pointer, a BOOL instead of the UIView * and then used it in the inherited class: the problem also occurs
EDIT (2):
I have no properties whatsoever in any of my classes and I still get the error.
I just added the properties for test porpouses, to see if a property in a parent class caused the same behaviour, and apparently it doesn't.
Something that is also weird is that when I get the error in the variable, I checked with my intellisense and it finds it...
In order to refer to an instance variable within any object other than self, including super, you must use the structure pointer operator (->). The default scope of an instance variable is protected, which means it can only be accessed within the class it is defined in or a subclass of that class. Since CustomViewController is a subclass of BaseViewController, this scope is sufficient to access the variable using self->_vwHeader, but if the second class you were trying to do this from is not a subclass you will also need to change the scope to either #public or #package.
In summary, change your method call to:
[self->_vwHeader setHidden:NO];
and it should work for any subclasses of the base view controller.
Do a clean and build, and also make sure you are not specifying a specific framework search path in the build settings. If you leave it empty you should get the correct libraries.
well I don't know, should work.
BaseViewController.h
#interface BaseViewController : UIViewController {
UIView *_vwHeader;
}
#property(nonatomic,retain)UIView *_vwHeader;
#end
BaseViewController.m
#synthesize _vwHeader;
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (void)loadView
{
[super loadView];
[self._vwHeader setHidden:NO];
}
#end
I faced similar problem as you. In my case the reason was (strangely!) wrong synthesization of properties in subclass.
Example:
In .h file of subclass you have following declaration
BOOL _flag;
...
#property (nonatomic, assign) BOOL flag;
while in you synthesize the property in the wrong way:
#synthesize flag;
instead of
#synthesize flag = _flag;
Strangely, the compiler does not complain about the wrong synthesization (the properties even work fine!), but raises an error, when I try to access protected fields declared in base class.
Detailed explanation
Here is what my code look like
I have base class (excerpt):
#interface BaseEditionModalController : NSObject
{
DataContext *_dataContext;
}
And I have subclass of it (excerpt):
#interface LocationModalController : BaseEditionModalController
{
MCLocation *_readLocation;
LocationCommModel *_oldLocationCommModel;
}
//This is MCLocation for reading only - from the main application context
#property (nonatomic, retain) MCLocation *readLocation;
#property (nonatomic, retain) LocationCommModel *oldLocationCommModel;
#end
And in the LocationModalController.m I have following wrong declarations:
#implementation LocationModalController
#synthesize readLocation;
#synthesize oldLocationCommModel;
Trying to access _dataContext in LocationModalController produced the error that _dataContext is undeclared.
Changing the synthesization of properties to:
#implementation LocationModalController
#synthesize readLocation = _readLocation;
#synthesize oldLocationCommModel = _oldLocationCommModel;
MAGICALLY SOLVES THE PROBLEM!
Regards
I just stumble upon your method declaration
-(void)loadView { ... }
In a view the first point you can rely that everything is fully initialized is after -(void)viewDidLoad was called. Maybe your code works on the simulator because your Mac is fast enough to cope this speed issue - but your mobile device isn't.
Maybe try this coding:
Your BaseViewController.h file:
#interface BaseViewController : UIViewController {
UIView *_vwHeader;
}
#end
Your BaseViewController.m file:
#import "BaseViewController.h"
#implementation BaseViewController
-(void)viewDidLoad {
[super viewDidLoad];
_vwHeader = [[UIView alloc] init];
}
Your CustomViewController.h file:
#interface CustomViewController : BaseViewController {
}
#end
Your CustomViewController.m file:
#import "CustomViewController.h"
-(void)viewDidLoad {
[super viewDidLoad];
[_vwHeader setHidden:NO];
}
Now your CustomViewController can rely on every instance variable in BaseViewController is correctly instantiated.
The error says that _vwHeader undeclared.
So try by modifying the code in:
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (void)loadView
{
[super loadView];
if(!_vwHeader)
{
_vwHeader = [[UIView alloc]init];
}
[_vwHeader setHidden:NO];
}
#end
It is possible that when you compile for target and for simulation the data members are either protected or private. Probably for target are private by default and this seems to cause the problem. Try out playing with the #private and #protected keywords.
However, I strongly suggest that you use properties even between your super/sub-classes. Having a complex structure is a bit hard to debug. Setting up a property will transfer the access to the data through the getter/setter methods (a breakpoint on #synthesize also works) and you will be able to see in the call stack who is accessing what.
Especially, the syntax #synthesize propertyName = prefixDataNameSufix; allows you to easily adjust your class interface style without having to modify your coding habits.
I had the exact same problem and it turns out that I did not remove an unused iVar/property in the SUBCLASS. Let's call it session. I removed _session from the iVar but I forgot to remove it from the properties, then in the .m file I had this synthesize session = _session. Once I removed them all, I can compile for iOS device without problems.
If you think your superclass is fine, look into your subclass, check your iVars and properties and synthesize section in the .m file
I had this exact problem.
In my case, I was relying on ivar synthesis of a property. That is, I did NOT declare UITextView *textView_, but I did #synthesize textView = textView_;
This builds fine on my iOS Simulator. However, my iOS device build fails, regardless of whether I use llvm or gcc.
When I add the declaration back to my interface:
#interface MyTableViewController : BaseTableViewController {
#private
UITextView *textView_; // this line is important!
}
Everything works fine!
See the answer from tc above. This is a bug in the compiler shipped with sdk 4.2.
Specifically I have seen the same error and if on the same machine I have sdk 4.2 and sdk 4.3 installed the error disappears (even if I compile for 4.2).
If anyone is having this issue after upgrading their tools and devices to iOS10, I had it and found that declaring them as weak, nonatomic in the .h file was the issue. I had never encountered this when doing so before but after removing (weak, nonatomic) from the property declaration, everything worked fine again.
At present I'm building a basic application to learn Objective-C and the iPhone SDK.
I'm creating NSObject with getters and setters to get to grips with how these works. I've successfully added a property and getters and setters to my main controller, currently I'm trying to create a separate class which I can create a new instance of within my controller but it crashes when I try and use the setter.
Thank you in advance for your time, sorry if this question is as stupid as I'm sure it is.
Here's the header for my class
QuizQuestion.h
#import <Foundation/Foundation.h>
#interface QuizQuestion : NSObject {
NSString *question;
}
#property (retain) NSString* question;
#end
QuizQuestion.m
#import "QuizQuestion.h"
#implementation QuizQuestion
#synthesize question;
- (void) dealloc
{
[question release];
[super dealloc];
}
#end
And here is my controller code (i've cut some out)
#implementation Quiz2ViewController
#class QuizQuestion; // Is this correct?
- (void)viewDidLoad {
QuizQuestion *aQuestion;
//gets here fine, but crashes (the app closes) when I set question.
[aQuestion setQuestion:#"hello world"];
[super viewDidLoad];
}
#end
As well as #class I tried import "QuizQuestion.h" and I get the same issue.
You're not actually allocating an instance of the QuizQuestion class—your aQuestion variable isn’t pointing to anything in particular, so trying to send it a message, like -setQuestion:, is sending that message to... well, there’s no telling where, and sending things messages that aren’t meant for them is a surefire way to crash your app. What you need to do is this:
QuizQuestion *aQuestion = [[QuizQuestion alloc] init];
[aQuestion setQuestion:#"hello world"];
You also need to call [aQuestion release] at some point, or you’ll leak the memory associated with it.
You need to allocate space and initialize the QuizQuestion.
QuizQuestion *aQuestion = [[QuizQuestion alloc] init];
before setting the question.
I think you need to read up on some documentation before you try any more coding. Apple has several intro programming guides that are very good. The problem you are having is addressed in this section:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html
QuizQuestion *aQuestion = [[QuizQuestion alloc] init];
[aQuestion setQuestion:#"hello world"];
[aQuestion release];
I have a C++ module I am using in my AppDelegate class. This all works fine. Now I need to talk to my appDelegate from my viewController, and this is causing problems.
I cannot include AppDelegate in my ViewController class and use [[UIApplication sharedApplication] delegate]. If I try, compiler goes berserk when it reaches c++ included in AppDelegate. If I rename my ViewController to .mm then it tries to parse AppDelegate.mm as c++.
Is there a way around this? Can I somehow dispatch an event from my ViewControler?
Wrap the C++ bit in
#ifdef __cplusplus__
...
#endif
Or rename the view controller source file from .m to .mm. Then it will be compiled as Objective C++.
If I rename my ViewController to .mm then it tries to parse AppDelegate.mm as c++.
that's not right. by default, the translation should (in this case) be treated as objc++. have you overridden this default behavior? i say this because i use a ton of objc++ -- it works/builds well.
anyhow... let's assume you have to work around this (which you shouldn't). this is useful because it can be better to abstract c++ from other sources (which may not be translated as c++ or objc++).
MONObject.h
/* our c++ class.
use a forward declaration so the c/objc translations don't
need to see the declaration of t_mon_object_data.
*/
struct t_mon_object_data;
#interface MONObject : NSObject
{
/* similarly, use a pointer as an ivar so the c/obj translations do not need
to see the declaration of t_mon_object_data.
use new/delete in your implementation of MONObject (which is objc++)
*/
t_mon_object_data* objectData;
}
- (void)manipulateTheDataWithThisString:(NSString *)string;
#end
MONObject.mm
#implementation MONObject
- (id)init
{
self = [super init];
if (nil != self) {
objectData = new t_mon_object_data;
if (0 == objectData) {
[self release];
return nil;
}
}
return self;
}
- (void)dealloc
{
delete objectData;
[super dealloc];
}
- (void)manipulateTheDataWithThisString:(NSString *)string
{
objectData->manipulateTheDataWithThisString(string);
}
#end
now objc clients may use via -[MONObject manipulateTheDataWithThisString:]. of course, you can always use C wrappers, if you prefer.
Go to project settings and try to change the 'Compile Sources As' to Objective-C++