singleton, public, or global variable use - iphone

I have searched and tried every example regarding singleton, public, and global variables in stack overflow on this subject. I'm making a mistake some where. I have a settings variable called strIP that is part of a textField and is declared in my secondViewController.h. I want this variable to used in a class called myWSupdate.m. It's just one variable I wanna pass it to a connection string. this compiles correctly but the app crashes on run. What am I doing incorrectly?
error from complier:Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[SecondViewController sharedIP]: unrecognized selector sent to class 0x6403c'
secondViewController.h
#interface SecondViewController : UIViewController
{
UITextField *ipAdd;
NSString *strIP;
}
#property (nonatomic, retain) IBOutlet UITextField *ipAdd;
#property (retain) NSString *strIP;
+(SecondViewController*)sharedIP;
then I call it in myWSupdate.m:
#import "SecondViewController.h"
/* Implementation of the service */
#implementation myWSupdate
- (id) init
{
if(self = [super init])
{
SecondViewController* IP = [[SecondViewController sharedIP]init];
NSLog(#"the test has %#", IP.strIP);
}
}
#end

Since strIP belongs to a SecondViewController, you need to reference it as part of that object.
How to do that depends on the relationship between SecondViewController and myWSupdate. (For example, if the controller creates a myWSupdate object, you could pass the variable as part of the init.)
The fact that it's marked public doesn't change the fact that it's an instance variable and therefore needs to be used in connection with an instance of its class.

Related

iOS - Set Model class to be delegate of another Model class

I have a CLLocationManager singleton which implements a protocol, so I can tell another model class (ServerConnection) that an updated location of the user has been found.
In my AppDelegate in the method, didFinishLaunching, I write
ServerConnection* serverConnection = [[ServerConnection alloc] init];
[LocationManager sharedLocationSingleton].delegate = serverConnection;
[[LocationManager sharedLocationSingleton] getUsersLocation];
This doesn't work and the delegate method in my ServerConnection class isn't called. However, if I try having my AppDelegate class be the listener, as in the following line, it works fine.
// self refers to AppDelegate
[LocationManager sharedLocationSingleton].delegate = self;
Here, my AppDelegate implements the required delegate method and the method is called when the user's location is updated, as it should.
Why is my above method failing, where I try to set the delegate to be serverConnection?
Tutorials online usually point to using a UIViewController or the AppDelegate as the "listener", but in my case, I want a separate model class to be the listener. How do I do that?
Below is my LocationManager singleton class with the protocol
#class LocationManager;
#protocol LocationManagerDelegate <NSObject>
#required
-(void)LocationManagerUpdated:(LocationManager*) locationManager
withValue:(CLLocation*) location;
#end
#interface LocationManager : NSObject <CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager* locationManager;
#property (strong, nonatomic) CLLocation* location;
#property (weak, nonatomic) id <LocationManagerDelegate> delegate;
+(LocationManager*)sharedLocationSingleton;
-(void) getUsersLocation;
#end
My header file for Server connection is.
#import <Foundation/Foundation.h>
#import "LocationManager.h"
#interface ServerConnection : NSObject <LocationManagerDelegate>
#end
This works when AppDelegate is set to be the listener, but not my model object ServerConnection. How do I fix this?
Thanks!
There should be no problem in doing what you are trying to do (i.e., having a non-controller class instance to act as a delegate).
This works when AppDelegate is set to be the listener, but not my model object ServerConnection.
Does your ServerConnection class implement the LocationManagerDelegate protocol? (I mean implement as opposed to just declare it in its interface).
Check the LocationManager method in charge for calling the delegate method (LocationManagerUpdated:) and add there a NSLog trace to check that the delegate object is correctly set when you try and send it the message.
EDIT:
ServerConnection* serverConnection = [[ServerConnection alloc] init];
[LocationManager sharedLocationSingleton].delegate = serverConnection;
[[LocationManager sharedLocationSingleton] getUsersLocation];
after you comment, it is clear that the issue stems from instantiating serverConnection in a stack variable and not in a property.
Your approach of making the delegate property a strong property is not correct since it leads to retain cycles. What you need to do is defining a strong serverConnection property in the class that executes the code I pasted above (the app delegate?).
If you don't mind my being rash, if you define the delegate as a strong property, what you are doing is fixing a bug by adding a second bug that hides the first one.
It looks like serverConnection is not retained anywhere and because delegate property is specified as weak, it is released and set to nil.
Check getUsersLocation method and see if delegate is nil at the moment you are trying to call LocationManagerUpdated:withValue:

Using class extensions in xcode 4.4

Since xcode 4.4 you don't need to #synthesize properties anymore (see here), the compiler does it for you. So, why does the compiler complain
use of the undeclared identifier _aVar
in my viewDidLoad method of ViewControllerSubclass:
#interface ViewController : UIViewController
#property (assign, nonatomic) int aVar;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.aVar = 5;
NSLog(#"Super value: %d", _aVar);
}
#end
#interface ViewControllerSubclass : ViewController
#end
#interface ViewControllerSubclass ()
#property (assign, nonatomic) int aVar;
#end
#implementation ViewControllerSubclass
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Subclass value: %d", _aVar);
}
#end
If I move everything to the one file instead of 4 separate files for the respective interfaces and implementations, the compiler instead complains that _aVar is private. But _aVar should have been automatically synthesized in my ViewControllerSubclass.
Still keeping everything in 1 file, if I move the initial property declaration to a class extension:
#interface ViewController ()
#property (assign, nonatomic) int aVar;
#end
The build still fails saying that _aVar is private.
If I go back to the 4 file setup for the respective interfaces and implementations xcode builds without even a warning.
If I then run the code:
[[[ViewControllerSubclass alloc] init] view];
the log statements in the above examples print out the following:
Super value: 0
Subclass value: 5
It makes sense that NSLog(#"Super value: %d", _aVar); produced a result of 0 because this variable is supposed to be private to the superclass. But then, why does NSLog(#"Subclass value: %d", _aVar); produce a result of 5??
This is all very odd.
You are confusing several different issues, and I'm somewhat confused when you talk about jumping between files and you don't specify where your errors are happening.
Anyway, there is the issue of instance variable visibility. If you declare your iVars within the interface scope, they are, by default, protected.
#interface Foo : NSObject {
int protectedInt;
#private
int privateInt;
#public
int publicInt;
}
#end
When you synthesize iVars, the instance variables themselves are private, unless you explicitly specify them.
Methods will always fire on the most derived implementation.
Now, when you call this...
[[[ViewControllerSubclass alloc] init] view];
You will allocate a subclass, initialize, and cause the view to be loaded. This code will execute...
#implementation ViewControllerSubclass
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Subclass value: %d", _aVar);
}
#end
The first thing it does is call the base class implementation...
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.aVar = 5;
NSLog(#"Super value: %d", _aVar);
}
#end
Of course, it calls super, but that part's not important here. The next line assigns 5 to self.iVar. But, which iVar? It calls the property setter method on this object. What type is this instance? It's a ViewControllerSubclass. Since you have given both your base class and its subclass the same name (and declared the property as part of the class extension), they each have their own private-scope instance variable .
However, a method is called on the most derived implementation. Thus, self.iVar will set the instance variable of the subclass. The instance variable for the base class remains unchanged.
When you NSLog the value, you are accessing the private instance variable of the base class, which has not been changed.
Now, after the base class viewDidLoad finishes, we get the code running for the subclass. It logs the value of its private instance variable, which was changed as a result of the base class calling the property setter. So, it will now print it's value, which is 5.
When you make the superclass declaration public, the compiler won't attempt to re-synthesize the property; it assumes that's been taken care of in the superclass. Thus, _aVar is not in scope anywhere in the subclass. It's private anyway, so even when you put them all in the same file that's why you see those errors.
However when you make the superclass property declaration inside the class extension, the compiler will auto-synthesize the property for both the superclass and the subclass. This ends up with both classes having private instance variables _aVar (with two distinct addresses). However, when the superclass viewDidLoad method sets the property, the method invokes the subclass's accessors, which set the value of the subclass's private _aVar variable, and not the superclass's. So that explains why you see the superclass value not changing.
Hope this helps!
I just tested your setup and could replicate your error. I came to the following conclusion:
You need to declare your #property in a .h file. If you want a private variable, declare it in .m in the category #interface (the one with the parentheses).

clarifying on properties in objective C

Sorry for the simple question.
When I see a definition of a property inside the h file, but outside of the class #interface scope, what does it mean ?
#property (nonatomic, readonly) RMMapContents *mapContents;
Here is the code:
#class RootViewController;
#class RMMapContents;
#interface MapTestbedAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
//MAIN VIEW
//==============
RootViewController *rootViewController;
// NETWORK DATA
// =============
NSMutableArray *photoTitles; // Titles of images
NSMutableArray *photoSmallImageData; // Image data (thumbnail)
NSMutableArray *photoURLsLargeImage; // URL to larger image
NSMutableData *receivedData;
NSURLConnection *theConnection;
NSURLRequest *request;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
#property (nonatomic, readonly) RMMapContents *mapContents;
#end
Inside a function I see this line:
- (void)foo:(xyz *)abc{
..
RMMapContents *mapContents = [self mapContents];
..
}
So, taking it from C++, the mapContents seem like it is not a global scope var (after all, that's why they call them properties, right?), but isn't defining the same name again inside the function weird a bit?
I hope someone can clarify a little here.
Thanks!
The scope of the #interface block extends upto the #end keyword and is not restricted to the braces {}.
So the #property declaration lies very much inside the scope of the #interface and like cli_hlt rightly answered, it acts like a substitute to setter and getter methods for the mapContents property.
so a property named mapContents, would have setters and getters which look like this :
- (void)setMapContents; //setter
- (RMMapContents *)mapContents; //getter
and would can be accessed from within the class using these methods:
[self setMapContents:newContents];
AND
RMMapContents *contents = [self mapContents];
Well, a property is not just a variable. A property is a variable plus its setter and getter methods. A property is usually said to be backed by a variable, which usually(but not always) has the same name as the property itself.
So there are basically three scenarios:
The developer has redefined the backing variable, look for something like:#synthesize mapContents=mapContents_, at the beginning of the implementation -> no problem here.
The compiler defined the variable to be something you don't now but not equal to mapContents - > no problem.
The property backing variable is indeed called "mapContents", so then the local definition hides the global definition (look for a compiler warning here). But by calling [self mapContents] you will not access the global variable but call the getter, which in turn will access the class variable (because then the local mapContents is out of scope)
Hope this helps.
global var mapContents is readonly,in foo function , create a new pointer,then you can change the value of inner var.
Look for a method in your class with a name mapContents that will return a initialization to your RMMapContents class.
Basically this line RMMapContents *mapContents = [self mapContents]; says that initializing an instance of RMMapContents called mapContens using the method mapContents.

Calling method in object gives SIGBART

Simple question, but this gives me an error and I can't seem to resolve it.
In my object (UIViewController) I have declared a method in the .h
-(void)setCurrentLoc:(CLLocation *)loc;
and in the .m
-(void)setCurrentLoc:(CLLocation *)loc
{
currentLocation = loc;
}
All good. But this is how I initiate this object (which works fine) but when I call setCurrentLoc: it gives SIGBART
.h
IBOutlet VectorViewController *vectorView;
.m
vectorView = [[VectorViewController alloc] init];
...
//In another method I call:
[vectorView setCurrentLoc:location]; // Error/SIGBART line
Is there something I missed? Maybe not making it global or something?
Error:
-[UIView setCurrentLoc:]: unrecognized selector sent to instance 0x841cab0
-[UIView setCurrentLoc:]: unrecognized selector sent to instance 0x841cab0
This means that you are sending the message to a pointer that is no longer pointing at your VectorViewController, but is now pointing at a UIView. This usually happens when the object has been autoreleased or released and the memory re-used.
Check carefully what happens between when you create vectorView and when you call it again in your other method. You seem to be setting and accessing the instance variable directly - you should probably be accessing it via properties (hard to be specific without knowing if you are using ARC or not) and making sure those properties are declared appropriately.
Based on the code you've written, you don't need to provide a setCurrentLoc method, use a property instead and synthesize instead.
in your VectorViewController class, use:
#property (nonatomic, retain) IBOutlet CLLocation * currentLocation;
in your #implementation, add:
#synthesize currentLocation;
and in your dealloc method, add:
self.currentLocation = nil;
Leave your existing setCurrentLocation call as is.

Unrecognized selector sent to instance" problem

my code broke somewhere along the way, and crashes when using the navigation bar buttons.
Error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView newMemoViewController:didAddMemo:]: unrecognized selector sent to instance 0x5b55a60'
When debugging, the program does run the cancel method, and throws an exception at the #synthesize line. However, I cannot see anything wrong with it.
The symptoms are identical, so I am including the relevant code only for the Cancel button:
NewMemoViewController.h
#import <UIKit/UIKit.h>
#protocol NewMemoDelegate;
#class AKVoiceMemo;
#interface NewMemoViewController : UIViewController {
#private
AKVoiceMemo *voiceMemo;
id <NewMemoDelegate> delegate;
}
#property (nonatomic, retain) AKVoiceMemo *voiceMemo;
#property (nonatomic, assign) id <NewMemoDelegate> delegate;
#end
#protocol NewMemoDelegate <NSObject>
- (void)newMemoViewController:(NewMemoViewController *)newMemoViewController didAddMemo:(AKVoiceMemo *)voiceMemo;
#end
NewMemoViewController.m
#import "NewMemoViewController.h"
#synthesize delegate;
- (void)viewDidLoad {
UIBarButtonItem *cancelButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStyleBordered target:self action:#selector(cancel)];
self.navigationItem.leftBarButtonItem = cancelButtonItem;
[cancelButtonItem release];
}
- (void)cancel {
[self.delegate newMemoViewController:self didAddMemo:nil];
}
Your help would be appreciated.
Edit: the delegate is the RootViewController:
- (void)newMemoViewController:(NewMemoViewController *)newMemoViewController didAddMemo:(AKVoiceMemo *)voiceMemo {
if (voiceMemo){
// Show the note in a new view controller
// TODO: Implement this
}
[self dismissModalViewControllerAnimated:YES];
}
You're probably setting the delegate of NewMemoViewController to a UIView object instead of an object that implements the NewMemoDelegate protocol.
The error message is telling you that a newMemoViewController:didAddMemo: message was sent to a UIView object and the UIView object didn't know what to do with it. Since your cancel method calls newMemoViewController:didAddMemo: on the delegate, it is the delegate which is the UIView object that doesn't recognize the newMemoViewController:didAddMemo: message. In other words, your delegate is a UIView and it doesn't implement the NewMemoDelegate protocol.
If you are correctly setting the delegate, then #jtbandes makes a great point: The delegate is probably being released and a UIView object is taking over the same memory location, thus "becoming" the delegate by accident. You're doing the right thing by using the assign attribute for your delegate; that's fairly standard Cocoa practice. However, you do need to make sure that the delegate is retained by another object, and that object needs to make sure that the delegate sticks around as long as NewMemoViewController needs it to.
I'm guessing you've over-released the delegate. I notice you have #property (assign) ... delegate;. This means that whenever you set the delegate, that object must be retained by something else as well.
The other possibility is the delegate is actually a UIView, but I'm guessing it's the other case.