Objective-c basics: Object declared in MyAppDelegate not accessible in another class - objective-c

I have an object declared in my app delegate:
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
ClassName *className;
}
In another class, I include the app delegate:
#import "MyAppDelegate.h"
#implementation AnotherClass
-(void)doMethod {
[className doClassNameMethod];
}
This fails at compile time due to className being undeclared. Shouldn't it be accessible, since I've included the MyAppDelegate, where className is declared? I need AnotherClass.doMethod to be accessing the same instance that is created in MyAppDelegate.
Any help on this would be most appreciated. Thanks.

How should your other class know about this variable in the first place?
What you have posted here, shows that instances of your AppDelegate class have a member named className, that is of type ClassName. This means that in every instance-method of the AppDelegate-class (the ones starting with a minus sign) you can access that variable by the name className.
This, however, does not mean you can directly access this variable from anywhere else! In fact, the exact opposite is much closer to the truth.
If you want to access that variable from somewhere else, there are a couple of options — the probably most common one would be to provide an accessor method for it (and for doing this, there are again a couple of options).
Consider the following:
#interface ClassA : NSObject {
NSMutableString *interestingMember;
NSMutableString *inaccessibleMember;
}
-(NSMutableString*)interestingMember;
#end
#interface ClassB : NSObject {
}
-(void)appendString:(NSString*) toMemberOfObject:(ClassA*);
#end
#implementation ClassB
-(void)appendString:(NSString*)string toMemberOfObject:(ClassA*)object
{
[[object interestingMember] appendString:string]; //this will work: you can access the variable through its accessor
[inaccessibleMember length]; // this will give a compile error, because the variable is undefined in the current scope
}
#end
Since this is the fairly basic bread & butter stuff of OOP, I'd encourage you to read Learning Objective C: A Primer and some of the other introductory material on Apple's website.

To access a instance variable in one class from another class, you should create a property.
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
ClassName *className;
}
#property (nonatomic, readonly) ClassName *className;
...
#end
From the other class you may then access this property like this:
#implementation AnotherClass
- (void) doMethod {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.className doClassNameMethod];
}
#end

The className is not within the scope of AnotherClass, it does not inherit anything from MyAppDelegate, you can create a MyAppDelegate object in AnotherClass and utilize the className variable for your uses but you still need to use the accessor methods in MyAppDelegate to talk to it.
TLDR: #import "MyAppDelegate.h" only allows you to create a MyAppDelegate object in AnotherClass not use the instance variables within that class.
What are you trying to do exactly within these two classes?

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 = ...

Instantiating multiple objects of the same class in Interface Builder results in shared property

I am trying to use NSPopUpButtons in my OSX program. In order to use KVO for its string and its index, I wrote a custom class (DLPopUpButtonManager).
#interface DLPopUpButtonManager : NSObject
#property NSArray *contentArray;
#property NSString *selectionString;
#property NSNumber *selectionIndex;
#end
That class works fine, when used only once in the program. But…
When I use more than one instance their contentArray is shared, meaning the two contentArrays point to the same instance. Huh?? That totally confuses me.
(Encapsulation, etc.)
I have two NSPopUpButtons that each are connected to an objects of class DLPopUpButtonManager. Those two classes are instantiated in Interface Builder though two objects. And in my AppDelegate I initialize them.
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet DLPopUpButtonManager *pUBM_1;
#property (weak) IBOutlet DLPopUpButtonManager *pUBM_2;
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self.pUBM_1 setContentArray:[NSArray arrayWithObjects:#"Female", #"Male", nil]];
[self.pUBM_2 setContentArray:[NSArray arrayWithObjects:#"Tall", #"Short", nil]];
[self showDetails:nil];
}
I find that both instances (confusingly and unwanted) use the same contentArray.
I investigated using breakpoints and saw that I have indeed two separate instances of DLPopUpButtonManager, but their contentArrays have the same pointer value.
Printing description of $20: <DLPopUpButtonManager: 0x6080000100b0>
Printing description of $23: <DLPopUpButtonManager: 0x6080000100c0>
Printing description of $25: <__NSArrayI 0x600000223ba0>
(
Tall,
Short
)
Printing description of $24: <__NSArrayI 0x600000223ba0>
(
Tall,
Short
)
(lldb)
I can’t find anything like that through Google or here on SO. Who can tell me, what I am doing wrong here?
I uploaded a little sample program to GitHub (https://github.com/donnerluetjen/PopUpButtonEtude).
Thanks for any input on that issue.
Try moving the underlying ivars for your your array and selection index properties into a private extension in the .m file, to ensure that they are not in fact static variables.
#interface DLPopUpButtonManager (){
NSArray *_contentArray;
NSUInteger _selectionIndex;
}
Thanks to tjboneman I could solve my problem, and I read some more about instance variables and static instance variables. Here is what I found after some serious searching:
From Apple's docs, The Objective-C Language | Defining a Class:
Class Interface
...
Note: Historically, the interface required declarations of a class’s instance variables, the data structures that are part of each instance of the class. These were declared in braces after the #interface declaration and before method declarations:
#interface ClassName : ItsSuperclass
{
// Instance variable declarations.
}
// Method and property declarations.
#end
Instance variables represent an implementation detail, and should typically not be accessed outside of the class itself. Moreover, you can declare them in the implementation block or synthesize them using declared properties. Typically you should not, therefore, declare instance variables in the public interface and so you should omit the braces.
...
Class Implementation
The definition of a class is structured very much like its declaration. It begins with an #implementation directive and ends with the #end directive. In addition, the class may declare instance variables in braces after the #implementation directive:
#implementation ClassName
{
// Instance variable declarations.
}
// Method definitions.
#end
Thanks again, tjboneman for pointing me in the right direction.

How to use #property correctly (Setters) within another class

another question i'm trying to use a setter within another class but I seem to get this odd error here is the code below:
AppDataSorting.h
#import <Foundation/Foundation.h>
#interface AppDataSorting : NSObject{
NSString *createNewFood;
NSNumber *createNewFoodCarbCount;
}
#property (readwrite) NSString *createNewFood;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)saveData:(id)sender {
NSLog(#"%#", self.foodName.stringValue);
self.createNewFood = self.foodName.stringValue;
NSLog(#"%.1f", self.carbAmount.floatValue);
}
#end
I get the error message in AppDelegate.m which is: Property 'createNewFood' not found on object of type 'AppDelegate *'
Could someone please explain the issue here?
You declare this property:
#property (readwrite) NSString *createNewFood;
In AppDataSorting.h so you can access it like self.createNewFood in AppDataSorting.m file not AppDelegate.m. If you want to call it as you do in AppDelegate.m you have move this line:
#property (readwrite) NSString *createNewFood;
to AppDelegate.h file.
Or if you want to use property from AppDataSorting class in AppDelegate you have to create object and call it on that object:
- (IBAction)saveData:(id)sender {
NSLog(#"%#", self.foodName.stringValue);
AppDataSorting *dSorting = [[AppDataSorting alloc] init];
dSorting.createNewFood = self.foodName.stringValue;
NSLog(#"%.1f", self.carbAmount.floatValue);
}
In -saveData:, self refers to an instance of NSAppDelegate. The createNewFood property is defined on instances of the class AppDataSorting.
Also note that Cocoa/CF naming conventions give special meaning to methods that start with "init", "new" and (to a lesser degree) "create". You probably want to avoid such things in your property names. Details here.
In general, properties should represent conceptual "properties" of an object. So if you had a Person class, it might have a name property, but it wouldn't have a createNewOutfit property.
You need to access createNewFood on an instance of AppDataSorting - but you're trying to access the property on the AppDelegate-class which clearly doesn't implement it.
So you would need to create an instance of AppDataSorting and then access the property like so:
AppDataSorting *instance = [[AppDataSorting alloc] init];
instance.createNewFood = self.foodName.stringValue;
Final notes:
The docs provide a good base of information
If you don't need atomicity you should always declare properties with the nonatomic attribute
createNewFood is not a good name for a property since it suggests a method which creates new food - yet it's only meant to store data (in this case an NSString instance)

Error accessing generated ivars when I override setters and getters in Modern Objective-C

I know now the new Objective-C compiler lets you not need to synthesize your properties anymore. I have one file that has two classes in it. My .h for a simple helper class looks like this:
#interface ViewFrameModel : NSObject
#property (nonatomic, strong) UIView *view;
#property (nonatomic, assign) CGRect frame;
- (id)initWithView:(UIView *)view frame:(CGRect)frame;
#end
In the same .h file, for my other class (class 2), I have:
#property (nonatomic, strong) ViewFrameModel *viewFrameModel;
In class 2.m, I can do this:
- (void)setViewFrameModel:(ViewFrameModel *)viewFrameModel {
_viewFrameModel = viewFrameModel;
[self pushViewFrameModel:viewFrameModel];
}
This works fine with no complaints from the compiler, however, when I add this:
- (ViewFrameModel *)viewFrameModel {
return _viewFrameModel;
}
I get two complaints, one on the first method setViewFrameModel:
"Use of undeclared identifier _viewFrameModel, did you mean viewFrameModel"
And the other on return _viewFrameModel:
"Use of undeclared identifier _viewFrameModel, did you mean viewFrameModel"
"Reference to local variable viewFrameModel' declared in enclosing context"
Why do I get these errors when I add in the
- (ViewFrameModel *)viewFrameModel {
return _viewFrameModel;
}
method? I want to override this method with some custom info, but it's complaining at me :-. Thoughts? TIA.
If you override both the setter and the getter, the compiler will not automatically create the instance variable for you anymore. You can add it to your class implementation like so:
#implementation ClassName {
ViewFrameModel *_viewFrameModel;
}
...
#end
Here is the results of some testing I did last year: iOS automatic #synthesize without creating an ivar.
In short, you need to use #synthesize or declare an iVar explicitly.
To summarize the answers:
If you override both the setter and the getter, the compiler will not create the instance variable for you.
Why? In that case, the compiler assumes that the property is dynamic: that it might be a property that relies on other properties for storage / computation, or that it will be created in other ways, for example, at runtime using Objective-C runtime functions.
To help the compiler understand the situation better there are two potential solutions:
#implementation Class
#synthesize property = _property;
...
#end
or
#implementation Class {
PropertyClass *_property;
}
...
#end

How do I call a class function from within an object within the class?

Basically, I want to call a function of a main class within a subclass that the main class holds a pointer to.
I know I can just get the main class to initiate the subclass and attach the function to it. But I do not want that, I want the subclass to initiate itself with its init function.
Is there any way I can call doSomething from someClass?
Some basic code to show what I want:
In MainViewController.h:
#class SomeClass;
#interface MainViewController : UIViewController {
SomeObject *someInformation;
SomeClass *someInstance;
}
#property (nonatomic, strong) SomeClass *someInstance;
-(void)doSomething:(id)sender;
#end
In MainViewController.m:
#implementation MainViewController
#synthesize someInstance;
-(void)doSomething:(id)sender {
//do something to someInformation
}
#end
In SomeClass.h:
#interface SomeClass : NSObject {
UIStuff *outstuff;
}
#property (strong, nonatomic) UIStuff *outstuff;
-(void)somethingHappened:(id)sender;
#end
In SomeClass.m
#implementation SomeClass
#synthesize outStuff;
-(IBAction)somethingHappened:(id)sender {
//call doSomething to the main class that points to this class
}
#end
Your terminology is shaky. Classes do not "hold" other classes. In your case, instances of class MainViewController have pointers to objects of class SomeClass. I am not being pedantic; poor terminology such as this casts doubt on one's understanding of underlying and important concepts.
That said, SomeClass objects need a reference to a MainViewController object if you want a SomeClass object to be able to send a message to a MainViewController instance. From the code you have posted, no such reference exists. You either need to expand the SomeClass interface to store an explicit reference to a MainViewController object, or you can employ something slightly more indirect (at least conceptually) via delegation. However, because you have not provided case-specific information, our solutions will be formed lacking detail-derived insight.