How to Access parentViewController's methods and properties - objective-c

I've been searching for a solution for this problem but I couldn't solve it!
I tried
mapViewController = (MapViewController*)self.parentViewController;
but it doesn't work.

you can do like this ..
in your class .h file
make a member variable
#interface SomeClass: NSObject
{
UIViewController *controller;
}
when you initialize the class make it controller to self ...that way it will work the same way as
SomeClass *class = [[SomeClass alloc]init];
class.controller = self;
now you can use the parent properties and methods..However you will still need to typecast it to (MapViewController*)controller so that it autocompletes its methods.

Related

What is the proper way to declare a global variable in Objective-C

So, I was wondering what the proper way to declare a global variable is in an iOS Project.
I don't want it set as a property, because the variable should not be accessible from outside the class.
I am going to provide a few ways I have seen, let me know which is the proper way, and if there is another way that is better.
This way I add the global variable inside curly braces after the #interface declaration in the implementation file .m. Then I can initialize the variable in the viewDidLoad
#import "ViewController.h"
#interface ViewController () {
int globalVariableTest;
}
#end
#implementation ViewController
Another way I add the global variable inside curly braces after the #implementation declaration in the implementation file .m. Again intializing in the viewDidLoad
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController {
int globalVariableTest;
}
Another way is adding the variable after the #implementation without the curly braces, also this allows me to set the intial value without the viewDidLoad
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
int globalVariableTest = 1;
Another way is to add the variable after the #interface inside the header file .h
#interface ViewController : UIViewController
{
int globalVariableTest;
}
So if there is a better way please let me know, all help will be appreciated!
Declaring variables inside curly braces is actually declaring an instance variable or "ivar" for short. That is, a variable that's local to instances of your class.
This used to only be possible after #interface declarations, which is why you sometimes see it there. This changed around Xcode 4 so that you can now do it after #implementation also. As far as I'm aware, this is just stylistic preference. ivars are never accessible outside a class (in theory. Technically, everything is accessible to everything in C), so defining them in the .h won't make them public. It does expose an implementation detail, though, which is why most code I see now that uses them puts them in the #implementation.
But I don't see them much in code anymore. Because when you define a #property what is actually happening under the covers is an ivar, a getter method, and a setter method are all actually being synthesized for you. The getter and setter methods just get the value of the ivar and set the value of the ivar, respectively.
So if what you want is something that has the same scope as a property, but doesn't come with the -myVar and -setMyVar: methods, then this is the right way to go.
But you probably shouldn't want that. There are a whole bunch of reasons that it's nice to only access ivars through accessor methods. It lets you override functionality, translate values, and all the other sorts of fun things abstraction affords you.
If what you want is a #property that isn't accessible outside the class, just declare it in a class extension:
//In MyClass.m
#interface MyClass()
#property NSNumber *myProperty;
#end
#implementation MyClass
//All your implementation stuff here.
#end
Because it's not in the .h file, it won't be "visible" to other classes (In theory. See above about everything being visible in C).
If on the other hand, what you really truly want is something that is really truly global (hint: you shouldn't. Global variables are generally a smell of bad design), you need to define it at the top of your file outside any #interface or #implementation blocks.
Another related tidbit: To define a "global" variable with a scope limited to a given file, look into C's static keyword. It's interesting.
You can use a singleton class to create/share (read / write) all variables across different classes (view controller).
.h
#interface SharedVariables : NSObject {
NSDictionary *dicti_StackSites;
NSDictionary *dicti_UserMe;
}
#property(nonatomic, strong) NSDictionary *dicti_StackSites;
#property(nonatomic, strong) NSDictionary *dicti_UserMe;
+(id)sharedVariablesManager;
#end
SharedVariables.m
#import "SharedVariables.h"
#implementation SharedVariables
#synthesize dicti_StackSites;
#synthesize dicti_UserMe;
+(id)sharedVariablesManager {
static SharedVariables *sharedVariablesClass = nil;
#synchronized(self) {
if (sharedVariablesClass == nil) {
sharedVariablesClass = [[self alloc] init];
}
}
return sharedVariablesClass;
}
-(id)init {
if (self = [super init]) {
dicti_StackSites = [[NSDictionary alloc] init];
dicti_UserMe = [[NSDictionary alloc] init];
}
return self;
}
-(void)dealloc {
}
#end
Usage from any other class
#import "SharedVariables.h"
SharedVariables *sharedManager = [SharedVariables sharedVariablesManager];
//to get sharedManager.dicti_StackSites
//to set sharedManager.dicti_StackSites = ...

Access Class without initializing

I want to create a class in objective-c with its methods, so that for accessing the data I don't want to instantiate the class. how can I do it?
Either you can use singleton, or if you are planning to use only static methods, you can just add it in the class and use it directly with class name.
Create methods as static,
+(void)method;
then use it as,
[MyClass method];
This is helpful only if you are creating some utility classes which has only some utility method like processing an image or so. If you need to have property variables, you will need singleton.
For eg:-
Go to new file and create MySingleton class which will create MySingleton.h and MySingleton.m files.
In .h file,
#interface MySingleton : NSObject
{
UIViewController *myview;
}
#property (nonatomic, retain) UIViewController *myview;
+(MySingleton *)sharedSingleton;
In .m file,
+ (MySingleton*)sharedSingleton {
static MySingleton* _one = nil;
#synchronized( self ) {
if( _one == nil ) {
_one = [[ MySingleton alloc ] init ];
}
}
return _one;
}
- (UIViewController *)myview {
if (!myview) {
self.myview = [[[UIViewController alloc] init] autorelease]; //you can skip this, but in that case you need to allocate and initialize the first time you are using this.
}
return myview;
}
Then use it as,
[[MySingleton sharedSingleton] myview] anywhere in your project. Remember to import MySingleton.h though. Similarly you can create any object in singleton and use it. Just implement the getter or setter method accordingly.
One thing you have to be careful is that the object created in a singleton has only a single memory space allocated and hence it is the same object whenever you are using anywhere in your project. The above code will not create multiple copies of myview object in the class. So whenever you are modifying a property of myview that will be reflected everywhere. Use this approach only if it is absolutely needed and you need to have access to a single object from all over the project. Normally we use this only for situations like storing a sessionID which needs to be accessed from different classes etc..
You may use singleton pattern, check this question.
Like this:
+(MySingleton *)sharedInstance {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
shared.someIvar = #"blah";
});
return shared;
}
Or if you want to just access methods, you may use factory methods (those with +, not with -)
#interface MyClass
#property (nonatomic, assign) NSInteger value;
+ (void) factoryMethod;
- (void) instanceMethod;
...
// then in code
[MyClass factoryMethod]; // ok
[[MyClass sharedInstance] instanceMethod]; // ok
[MyClass sharedInstance].value = 5; // ok
UPDATE:
You may add a property to appDelegate
// in your app delegate.h
#property (nonatomic, retain) UIViewController* view;
// in your app delegate.m
#synthesize view;
and get appDelegate from almost any place like:
myapp_AppDelegate* appDelegate = [[UIApplication sharedApplicaton] delegate];
appDelegate.view = ...; // set that property and use it anywhere like this
Note, that you'll need to #import your UIViewController subclass and your appDelegate.h to make autocomplete work and sometimes avoid warnings.
// someFile.m
#import "appDelegate.h"
#import "myViewController.h"
...
myapp_AppDelegate* appDelegate = [[UIApplication sharedApplicaton] delegate];
appDelegate.view.myLabel.text = #"label text";

Objective-C: why a custom object will be a zombie

I'm developing an app in Objective-C using ARC.
My simplified code looks like this:
ClassA (.m)
MyCustomClass *obj = [[MyCustomClass alloc] initWithValue1:#"abc" value2:1000];
MyViewController *vc = [[MyViewController alloc] initWithObject:obj];
// "vc" will become the first item of a UITabBarController
MyViewController (.h)
- (id)initWithObject:(MyCustomClass *)obj {
...
localReferenceToOjbect = obj;
...
}
- (void)viewWillAppear:(BOOL)animated {
// do something with "localRefernceToObject" <---
}
launching the app will result in a call to a zombie: when the ViewController is shown, the "obj" will be already deallocated and so i can't use it anymore.
my workaround is:
ClassA (.h)
#interface ClassA : UIViewController {
MyCustomClass *obj;
}
ClassA (.m)
obj = [[MyCustomClass alloc] initWithValue1:#"abc" value2:1000];
MyViewController *vc = [[MyViewController alloc] initWithObject:obj];
// "vc" will become the first item of a UITabBarController
is this the right way?! i don't think so: why i've to store an istance of an object that is useless for ClassA?
i can't get an explanation on what's actually happening. could you help me?
You're right in the fact that it is not logical to keep around a reference to obj in ClassA.
But if you need to keep around the reference to obj for MyViewController to use it, retain it in MyViewController, not in ClassA, because that's MyViewController that will use it.
The easiest way to do this is to transform your localReferenceToObject you use in MyViewController into a #property(retain) propertyToObject; (or #property(strong) propertyToObject if you use ARC) and access it in your MyViewController.m with self.propertyToObject (instead of localReferenceToObject, to be sure to call the property's setter and thus really retain the object).
This way, the object will be retained and kept around while your MyViewController instance is still alive.
[EDIT] If you want this property to be private, you can declare it in the class extension so that it is not accessible from other classes, as in the below example. See here in Apple's documentation for more details.
In your MyViewController.h header file
#interface MyViewController : UIViewController
// Here you write the public API in the .h / public header
// If you don't want your property to be visible, don't declare it there
#end
In your MyViewController.m file
#interface MyViewController ()
// This is the private API, only visible inside the MyViewController.m file and not from other classes
// Note the "()" to declare the class extension, as explained in Apple doc
#property(nonatomic, retain) MyCustomClass* referenceToObject; // Note: use strong (which is a synonym of retain) if you use ARC
#end
#implementation MyViewController
#synthesize referenceToObject = _referenceToObject; // not even needed with modern ObjC and latest LLVM compiler
- (id)initWithObject:(MyCustomClass *)obj
{
self = [super init];
if (self) {
...
self.referenceToOjbect = obj;
...
}
return self;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// do something with "self.refernceToObject"
}
// This memory management code is only needed if you don't use ARC
-(void)dealloc
{
self.referenceToObject = nil; // release memory
[super dealloc];
}
Personally, as suggested by Apple in some WWDC sessions, I now really rarely use instance variables and prefer the use of properties instead, either public in the .h or private in the .m.
If you use ARC, you can still use an instance variable instead of a property as ARC will retain it for you, but as long as you make sure your instance variable is declared as strong and not weak.

About moving few methods to the superclass

I need to move the same method from 4 different classes to the superclass.
Such methods are exactly the same except for the type of a variable declared in them:
For example, in the method in the first class I have
FirstClass var = [[FirstClass alloc] init]
in the second class
SecondClass var = [[SecondClass alloc] init]
and so on.
What's the best way to implement this variation in the superclass ?
Should I use NSClassFromString in the superclass and get each string from each method in the subclasses?
thanks
I'm not 100% sure I get what you mean. So I could be answering the wrong question
If inside your class you need to use an object (I've called it worker below) to do your work, but the class of this object is not known til later, you can use dependency injection (DI).
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, retain) id<WorkerInterface> worker;
#end
MyClass.m
#implementation MyClass
#synthesize worker = _worker;
- (void)myMethod;
{
[self.worker doSomething];
}
// You could also provide a default class to use if one is not passed in
//
// - (id<WorkerInterface)worker;
// {
// if (!_worker) {
// _worker = [[DefaultWorker alloc] init];
// }
// return _worker;
// }
#end
Now whenever I instantiate this class I can simply pass in the appropriate object to be used e.g:
MyWorkerClass *worker = [[MyWorkerClass alloc] init]; // <- Conforms to #protocol(WorkerInterface)
MyClass *instance = [[MyClass alloc] init];
instance.worker = worker;
[instance doSomething];
If all the different types of iVar's you intend on initializing in the subclasses are descended from a common class, then I'd store that class in the super, or else just store it as an id. Then, setup a property accessor in each of your subclasses the casts the iVar as you need it.
#interface superClass : NSObject{
id _superIvar;
}
#end
#implementation superClass : NSObject
....super's code....
#end
Now in the implementation of the subclass declare a property in a category, shown below (or in the interface, if you want it public)
#interface subClass (private)
#property (strong) ClassType *superIvar;
#end;
#implementation
- (void) setSuperIvar:(ClassType *)superIvar{
_superIvar = superIvar;
}
- (ClassType *) superIvar{
return (ClassType *) _superIvar;
}
- (void) someMethodThatUsesSuperIvar{
[self.superIvar doSomething];
}
#end
Alternatively, if you don't want to open your _superIvar to direct access, you can set a property on the superclass and access through the property on the subclass. But in this way you can easily access super's ivars cast to the appropriate type.

Referencing UIViewController in NSObject

I am trying to access UIViewController's view in a NSObject class. The NSObject class is suppose to programmatically change UIViewController's user interface. I tried accessing NSViewController's view in NSObject by using self.view but was told that "request for member 'view' in something not a structure or union.
After a few inquiries, I was told that I need to reference viewController in my NSObject class. I am unsure of how to do this and would appreciate any help or point in the right direction. Thanks!
You should give the NSObject class a property called myController or something in its interface declaration, or pass a ViewController* to any methods that need to access it.
For the property, you can say:
ViewController* myController;
in the NSObject sub-class interface declaration, or for the method way, add an argument to your NSObject sub-class' method:
- (void) someMethodThatTakesAViewController: (ViewController*) theViewController {
//Do your stuff here
theViewController.view = [[UIView alloc] init]; // Or whatever you want to do
}
Hope this was what you were looking for.