Why can't I access properties of my class from within a C function?
This is what I mean:
This is my code:
#interface MandelGenerator : NSWindow <NSWindowDelegate>
{
//Displays the image
IBOutlet JSImageView *imageView;
}
#end
/
#import "MandelGenerator.h"
#implementation MandelGenerator
void test(void) {
imageView = nil;
}
#end
A C function, unlike the instance method of a class, has no association with an Objective-C class. Just being in the same .m file as the class means nothing -- unlike an instance method, which is called on an instance by means of sending a message to it (thus providing the context necessary to know which imageView of all the imageViews that might exist as properties of instances of MandelGenerators), a C function isn't called on anything and can only "see" global variables and variables that are passed in to it as parameters. If you want this to work, you would need to change that C method to
void test(MandelGenerator* generator) {
generator.imageView = nil;
}
Assuming that imageView is actually a property (it looks like an ivar to me).
Related
This is a common topic but, in my case there is one thing I don't understand that I can't find explained in the other asked questions.
Here is the gist of what I'm trying to do:
User clicks a button and something like this is called:
#implementation FirstClass
-(void)clickedButton
{
[SecondClass changeText];
}
And then in SecondClass is:
#implementation SecondClass
- (void)changeText {
[myLabel setText:#"text"];
}
So when the user clicks the button, the text property in myLabel in SecondClass changes to "text".
The only problem I have with this is calling [SecondClass changeText] on the existing instance of SecondClass. Since I'm not initializing the CCNodes programmatically (they are all automatically loaded upon running the app), I don't know where or how SecondClass is initialized. I'm using SpriteBuilder to build this project.
Any help would be appreciated. Thanks!
So, you have two instaces -- one with a button, and one with a label. I'm assuming they are both descendants of NSViewController or otherwise manage underlying views.
The problem is, you found no way to address second instance containing label from the method of first instance.
You need to define a property in first instance's class:
#property(weak) SecondClass *secondInstance;
And then in button clicked method:
-(void)clickedButton
{
[self.secondInstance changeText];
}
There is one issue left: who is responsible to set first instance's property that we defined? This depends on who did create both of them, probably just app delegate or enclosing controller, you know that better.
UPD: If both of the controllers are created by AppDelegate:
#import "FirstClass.h"
#import "SecondClass.h"
#interface AppDelegate ()
// case A - manual
#property(strong) FirstClass *firstInstance;
#property(strong) SecondClass *secondInstance;
// case B - declared in xib
//#property(weak) IBOutlet FirstClass *firstInstance;
//#property(weak) IBOutlet SecondClass *secondInstance;
#end
#implementation AppDelegate
...
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Create them
self.firstInstance = [[FirstClass alloc] init...];
self.secondInstance = [[SecondClass alloc] init...];
// Or maybe they are declared in MainMenu.xib, then you do not create them
// by hand, but must have outlets for both. See case B above.
// Connect them
self.firstInstance.secondInstance = self.secondInstance;
...
}
Note that class is not the same as an object (instance). Class is a named collection of methods, mostly for the instance. In Objective-C, class is not just a name, but an object too, so you can call a method on it (i.e. send an message to the class object). But here we always talk about objects (instances), so forget about classes – we hold objects via strong properties or weak outlets, depending on how they were created, and operate on objects, never on classes.
In objective C, the methods are either instance methods or class methods. As the name suggests, the instance methods require an instance of the class to work, whereas the class methods can be used with just the name of the class. What you need here is a class method. Just change the following line in your code:
#implementation SecondClass
- (id)changeText {
to
#implementation SecondClass
+ (id)changeText {
This will change the method from an instance method to a class method.
I have a base class lets say BaseClass which does some logic and handles gestures. I have another class FooBarClass which provides the view and is also a subclass of BaseClass, (FooBar : Base).
I know that I can call methods in super class by super methodName. I am stuck in a situation now, all of views are designed like these and now I need to pass message from FooBar to Base.
Is that possible ? If so how ? Should I be using NSNotifications or is there any better way to do it ?
If you are creating instance of subclass, which in your case is FooBarClass, you need not worry about message passing from super class to subclass. With inheritance, whatever properties, methods are exposed in header file (.h) of BaseClass, can be accessed from FooBarClass. If the methods belonging to BaseClass has been overridden in FooBarClass, then you have to explicitly make use of super otherwise, you can directly call self. However, if the properties belonging to BaseClass has been overridden in FooBarClass, then that variable will be holding the value which has been stored last. That is the reason why usually, properties are never overridden as it gets confusing.
Lastly, there is no need for NSNotification.
Ex: BaseClass.h
#interface BaseClass : UIView
- (void)runTest;
- (void)sayHi;
- (void)sayHi2;
#property (assign, nonatomic) NSInteger commonVar;
#end
BaseClass.m
- (void)runTest
{
self.commonVar = 100;
}
- (void)sayHi
{
NSLog(#"Hi from super");
NSLog(#"In super variable = %d", self.commonVar);
}
- (void)sayHi2
{
NSLog(#"Hi from super2");
}
FooBarClass.h
#interface FooBaseClass : BaseClass
#property (assign, nonatomic) NSInteger commonVar;
#end
FooBarClass.m
- (void)runTest
{
self.commonVar = 1;
[super runTest]; // Now, commonVar variable will be holding 100 throughout.
[super sayHi];
[super sayHi2]; // Same as next line because there is no sayHi2 overridden.
[self sayHi2];
[self sayHi];
}
- (void)sayHi
{
NSLog(#"Hi from derived");
NSLog(#"In derived variable = %d", self.commonVar);
}
Hope this answer helps you.
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.
Since recent runtimes in iOS, we are able to define properties that will generate accessors for instance variables. From what I understand, it is not mandatory to declare the instance variable used since it will be automatically done for us.
For example, if I write:
#interface MyFirstClass
#property (readonly, nonatomic) int size;
#end
and in the .m
#implementation MyFirstClass
#synthesize size;
#end
Then an instance variable named "size" will be added for me and a method called "-(int)size" will be implemented.
The problem is that when I create a second class MySecondClass which is a subclass of MyFirstClass, it seems that I can't access the instance variable size within this subclass:
#interface MySecondClass : MyFirstClass
#end
#implementation MySecondClass
- (id)init {
if (self = [super init]) {
size = 10; // this yields and error
}
return self;
}
#end
Are the automatically created instance variables private? Is there a possibility to set them as protected so I can access them in subclasses?
I know there is the possibility to declare the instance variable myself, but I'm just wondering...
With a superclass like this it works: (Is it because it's expressly declared as protected?)
#interface MyFirstClass {
int size; // defined expressly and used as #protected
}
#property (readonly, nonatomic) int size;
#end
Thank you for your help!!
Nicolas.
Any instance variable not declared in the main interface is automatically private, and this cannot be overridden. If you try to use a scope modifier when defining instance variables in the implementation, you will get an error that the specification is inconsistent.
The reason for this is that there is usually only one class per implementation file, which means the compiler doesn't know about the instance variable when compiling other classes. If you have multiple classes in the same file, the compiler could know about it, but you still aren't allowed to override the scope. Possible reasons in this case could be for consistency, or just so the compiler doesn't have to look in so many places for instance variables.
Use:
self.size = 10;
That will map to setSize method.
In my application there are lot of view controller in some view controller some variables are there which i want to use in other classes .my variable is not present in application delegate file so i can i make it global to use every where in my application?
In my opinion, how about using singleton pattern? So when you want to use the variables of that class, just get instance and then use the variables.
#interface MySingletonViewController : UIViewController
{
//here your variables
int globalVariables;
}
#property (nonatomic, assign) int globalVariables;
+ (MySingletonViewController *)sharedSingleton;
#end
#implementation MySingletonViewController
#synthesize globalVariables;
static MySingletonViewController *sharedSingleton = nil;
+ (MySingletonViewController *)sharedSingleton
{
#synchronized(self)
{
if (sharedSingleton == nil)
sharedSingleton = [[MySingleton alloc] init];
return sharedSingleton;
}
}
#end
UIViewController is class actually, so we can do this way : ) Hope this helpful.
Sure you can, but using global variables through entire app is definitely broken architecture design.
As Objective-C based on C, you can define variable (in you case - pointer to class) in any *.m file outside implementation part as:
MyVeryOwnClass *g_MyVeryOwnClassPointer = nil;
And access it as:
extern MyVeryOwnClass *g_MyVeryOwnClassPointer;
/* do some operations with your pointer here*/
Or move extern declaration to header file.
PS: You can use singletons. They are not the best solution, but better then using raw variable.