Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Jun 17, 2013, 08:51 PM   #1
Blakeasd
macrumors 6502a
 
Join Date: Dec 2009
Class Method -> Function -> Instance Method?

Hello,

I am working on an application with Objective-C and Cocoa. I've gotten myself in a peculiar predicament. I have an NSView on an NSWindow. In the NSView's mouseDown: method I call a class method for another class.

Now jump over to this other class. The class method I called from the other class needs to do this: (webView is an object in this class)

Code:
[self.webView setHidden:TRUE];
Unfortunately, being a class method it can't call upon objects inside the class to perform methods.

I then (perhaps foolishly) came up with the idea on crafting a function to call from the class method. I then came to the same problem -- I can't call methods upon objects again. So I built an instance method to call from the function. Which I still can't do because I get 'undefined identifier self'.

This whole thing can be best explained by this diagram:

NSVIEW SUBCLASS CLASS

Code:
 [DHSwipeClipView openInfoPane];
DHSwipeClipView CLASS

Code:
+(void)openInfoPane{
    NSLog(@"To the function!");
    openInfoPaneFunction();
}

void openInfoPaneFunction(){

    NSLog(@"To the instance method!");
    //I CAN'T CALL THE openInfoPaneMethod here!!
}

-(void)openInfoPaneMethod{

    [self.webView setHidden:TRUE];
    
}
How can I call the instance method from the class method?

The premise of the question is how can I call the instance method from the class method?

Hopefully it isn't too difficult to understand. Any Help is Appreciated!
Blakeasd is offline   0 Reply With Quote
Old Jun 17, 2013, 10:13 PM   #2
chown33
macrumors 603
 
Join Date: Aug 2009
Where you have this code:
Code:
 [DHSwipeClipView openInfoPane];
is there or isn't there an instance of DHSwipeClipView?

If there isn't, then there needs to be one. Why? Because the following is an instance method:
Code:
-(void)openInfoPaneMethod{

    [self.webView setHidden:TRUE];
    
}
and self means the instance, and self.webView refers to an instance property (I presume).

Asking the class DHSwipeClipView to somehow conjure up an instance will be fruitless. You need an instance already, and it needs to have already had its webView property assigned. Without a valid webView property, the expression self.webView will be nil, and telling nil to setHidden:TRUE will be fruitless.

If there is an instance of DHSwipeClipView at the point where you have this code:
Code:
 [DHSwipeClipView openInfoPane];
then replace that line of code with this:
Code:
 [myInstance openInfoPaneMethod];
where myInstance is whatever variable name or expression is needed to refer to the instance.


This code:
Quote:
void openInfoPaneFunction(){
has no parameters, so there are no instances available in the function.

If you've stored an instance in a global or a static variable, then that is available in the function. Otherwise you need to pass a parameter.


Without seeing more code, I'm guessing you're fundamentally confused about the difference between a class (such as DHSwipeClipView), and an instance of that class. You make an instance by calling alloc on the class. You then initialize the instance. The idiomatic form of this is:
Code:
[[DHSwipeClipView alloc] init];
You are then generally expected to assign the init'ed instance to a variable, so it can be used.

Here's an example declaring a variable, with an initializer expression that alloc's and init's an instance:
Code:
 DHSwipeClipView * myInstance = [[DHSwipeClipView alloc] init];
You will then presumably do something to ensure myInstance has a valid webView property, or maybe that's done in the -init method. I'm just guessing; you'd have to post more code.

With the webView property valid, then this should work:
Code:
 [myInstance openInfoPaneMethod];

If the above doesn't make sense, go through it carefully.

If there's a specific thing you don't understand, ask about that specific thing.

If you don't understand the difference between a class and an instance of that class, go back to whatever you're learning from and restudy the part about instances vs. classes. This distinction is fundamental.
chown33 is offline   0 Reply With Quote
Old Jun 17, 2013, 10:38 PM   #3
Blakeasd
Thread Starter
macrumors 6502a
 
Join Date: Dec 2009
There isn't an instance of DHSwipeClipView because I call openInfoPane which is a class method and not openInfoPaneMethod which is an instance method. There's actually a difference here, though I realize my naming scheme is quite confusing in this case.
Blakeasd is offline   0 Reply With Quote
Old Jun 17, 2013, 10:55 PM   #4
ElectricSheep
macrumors 6502
 
Join Date: Feb 2004
Location: Wilmington, DE
Send a message via AIM to ElectricSheep
I'm curious as to what it is about your design that requires you to use a class method rather than instantiating an DHSwipeClipView.
__________________
15'' MBP (early 2011) | i7 3770k Hackintosh | i7 Mac Mini (late 2012) | iPhone 5 | iPad 3 (2012) | iPad mini | MacOS X 10.9.2
ElectricSheep is offline   1 Reply With Quote
Old Jun 18, 2013, 12:49 AM   #5
chown33
macrumors 603
 
Join Date: Aug 2009
Quote:
Originally Posted by Blakeasd View Post
There isn't an instance of DHSwipeClipView because I call openInfoPane which is a class method and not openInfoPaneMethod which is an instance method.
If there isn't an instance of DHSwipeClipView, then how could the code possibly call an instance method?

That's not a rhetorical question. It goes directly to the fundamental issue. There is no instance of DHSwipeClipView. Yet somehow you expect to be able to call an instance method of that non-existent instance. Somewhere, somehow, your logic is profoundly flawed.

You haven't described what the DHSwipeClipView class does, or is expected to do, or why there is both a class method and an instance method with so much similarity. You haven't shown the class's @interface, so no one knows what the webView property is or does. Without descriptions and explanations of your intent, no one can tell if the logical flaw is in the design, the concept behind the design, or in the implementation.

It's as if someone told me to saddle a horse, and pointed me to an empty stall. I'd say, "I see no horse here". I can't saddle a non-existent horse. Nor can I saddle the species Equus caballus (i.e. domesticated horse) because you can't put a saddle on a species, only on an instance of a species, i.e. an actual horse.


Quote:
There's actually a difference here, though I realize my naming scheme is quite confusing in this case.
I fully understand the difference between a class method, a function, and an instance method. The names are a bit odd, but that's nothing compared to the logical flaw of not having an instance, yet expecting to call an instance method of it.

Again, I suspect you don't fully understand the difference between a class, which has a class method, and an instance of that class, which has an instance method.
chown33 is offline   0 Reply With Quote
Old Jun 18, 2013, 10:49 AM   #6
PatrickCocoa
macrumors 6502a
 
Join Date: Dec 2008
I can see the method! Why can't the app?

Quote:
Originally Posted by chown33 View Post
Again, I suspect you don't fully understand the difference between a class, which has a class method, and an instance of that class, which has an instance method.
Possibly OP believes that since the method is there in his code, other parts of his code should be able to "call that method".

In other words, he's thinking about the code as he sees it in Xcode, not about the objects that exist while the code is running. After all, since the method he wants to call is right there and he can see it, why can't the running code see that method and call it? The answer is that the method only exists in the running code once an instance of the class that contains that method is created.
__________________
iMac 21.5", 3.06GHz, 4 GB, 2 TB HD.
iPod Touch 3G.
PatrickCocoa is offline   0 Reply With Quote
Old Jun 18, 2013, 11:55 AM   #7
gnasher729
macrumors G5
 
gnasher729's Avatar
 
Join Date: Nov 2005
Code:
+(void)openInfoPane{
    DHSwipeClipView* myView = .....;
    [myView.webView setHidden:YES];
}
You just have to add the missing bit of code. (And YES and NO are standard identifiers in Objective-C and Objective-C++. TRUE and FALSE are not standard identifiers in any C-like language).
gnasher729 is offline   0 Reply With Quote
Old Jun 18, 2013, 12:16 PM   #8
Blakeasd
Thread Starter
macrumors 6502a
 
Join Date: Dec 2009
I think I did a poor job of originally explaining the problem. There seems to be much confusion of the issue at stake.

Here is a section of the @interface for for the DHSwipeClipView:

Code:
+(void)openInfoPane;
void openInfoPaneFunction();
-(void)openInfoPaneMethod;
Also in the DHSwipeClipView I have an instance object of DHSwipeWebView:

Code:
    DHSwipeWebView *webView;
and part of the implementation of DHSwipClipView:

Code:
+(void)openInfoPane{
    NSLog(@"Here we go!");
    openInfoPaneFunction();
    //WISH I COULD CALL openInfoPaneMethod: here or call setHidden: on the DHSwipeWebView instance
}

void openInfoPaneFunction(){

    NSLog(@"Function has been activated");
    //WISH I COULD CALL openInfoPaneMethod: here or call setHidden: on the DHSwipeWebView instance
    
}

-(void)openInfoPaneMethod{

    [self.webView setHidden:TRUE];
    
}
Here is the MouseDownView implementation that receives the mouseDown:

Code:
-(void)mouseDown:(NSEvent *)theEvent{
    NSLog(@"Show info pane button has been clicked!");
    [DHSwipeClipView openInfoPane];
    
}
There is no instance of DHSwipeClipView in my MouseDownView class because in the mouseDown: method I am using the class method

which is this:

Code:
 +(void)openInfoPane
So when mouseDown: is triggered it calls that class method that you see above. (At the top of this reply is the implementation of what it does)

Class methods cannot call instances of objects inside of classes (webView is what I want to call setHidden: on). I'd like to be able to do this inside of the class method:

Code:
[self.webView setHidden:TRUE];
Unfortunately it has to be done inside of an instance method. I thought that if I put a function inside of the class method, that function could call the instance method with the code that acts on the webView.

Functions as I've no discovered can't do something like:

Code:
[self openInfoPaneMethod]
Did I explain the situation more clearly this time? Hopefully this isn't too confusing.
Blakeasd is offline   0 Reply With Quote
Old Jun 18, 2013, 12:22 PM   #9
ElectricSheep
macrumors 6502
 
Join Date: Feb 2004
Location: Wilmington, DE
Send a message via AIM to ElectricSheep
The fundamental question here is why is this:

Code:
+(void)openInfoPane
modeled as a class method, and not as an instance method?

EDIT:

It should be noted that the fact you are having to jump through so many hoops to call an instance method for an object which likely doesn't even exist is a huge flag that your approach is fundamentally off track. I suspect what you really should be doing is either using the instance method directly or implementing a singleton pattern.
__________________
15'' MBP (early 2011) | i7 3770k Hackintosh | i7 Mac Mini (late 2012) | iPhone 5 | iPad 3 (2012) | iPad mini | MacOS X 10.9.2

Last edited by ElectricSheep; Jun 18, 2013 at 12:28 PM.
ElectricSheep is offline   0 Reply With Quote
Old Jun 18, 2013, 12:32 PM   #10
Blakeasd
Thread Starter
macrumors 6502a
 
Join Date: Dec 2009
It is modeled as a class method in attempt to fit in with the "structure" of the DHSwipe classes. The DHSwipeClasses are some classes I downloaded that add the Chrome swipe navigation to a WebView. The DHSwipeClipView is the only place where I can call methods on the DHSwipe *webView. I have a WebView in my .xib interface (whose class is another class I created which is a subclass of DHSwipeWebView), but IBOutlets render useless on it for some reason.

The moral of my project seems to be that if I want to provide some action on my WebView, it must go through the DHSwipeClipView.

The DHSwipe classes are on GitHub
Blakeasd is offline   0 Reply With Quote
Old Jun 18, 2013, 04:42 PM   #11
Blakeasd
Thread Starter
macrumors 6502a
 
Join Date: Dec 2009
It seems that a more interesting issue has come up. Take a look at the interface of DHSwipeClipView:

Code:
#import <Cocoa/Cocoa.h>
#import "DHSwipeWebView.h"
#import "mWebView.h"

@interface DHSwipeClipView : NSClipView {
    CGFloat currentSum;
    NSTimer *drawTimer;
    BOOL canGoLeft;
    BOOL canGoRight;
    DHSwipeWebView *webView;
    BOOL isHandlingEvent;
    BOOL _haveAdditionalClip;
    NSRect _additionalClip;
    CGFloat scrollDeltaX;
    CGFloat scrollDeltaY;
    IBOutlet NSButton *showInfoButton;
}

@property (retain) NSTimer *drawTimer;
@property (assign) CGFloat currentSum;
@property (retain) DHSwipeWebView *webView;
@property (assign) BOOL isHandlingEvent;
- (id)initWithFrame:(NSRect)frame webView:(DHSwipeWebView *)aWebView;
+(void)openInfoPane;
void openInfoPaneFunction();
-(void)openInfoPaneMethod;
-(IBAction)showInfoPaneButton:(id)sender;
@end
Now the implementation:

Code:
@implementation DHSwipeClipView

@synthesize drawTimer;
@synthesize currentSum;
@synthesize webView;
@synthesize isHandlingEvent;

- (id)initWithFrame:(NSRect)frame webView:(DHSwipeWebView *)aWebView
{
    if(!(self = [super initWithFrame:frame]))
    {
        return nil;
    }
    self.webView = aWebView;
    
   
    [self releaseGState];
    
    return self;
}



- (void)resetAdditionalClip
{
    _haveAdditionalClip = NO;
}

- (void)setAdditionalClip:(NSRect)additionalClip
{
    _haveAdditionalClip = YES;
    _additionalClip = additionalClip;
}

- (BOOL)hasAdditionalClip
{
    return _haveAdditionalClip;
}

- (NSRect)additionalClip
{
    return _additionalClip;
}

// From https://gist.github.com/b6bcb09a9fc0e9557c27
- (NSView *)hitTest:(NSPoint)aPoint
{
    NSEvent *currentEvent = [NSApp currentEvent];
    NSScrollView *scrollView = [self enclosingScrollView];
    if([currentEvent type] == NSLeftMouseDown)
    {
        // if we have a vertical scroller and it accepts the current hit
        if([scrollView hasVerticalScroller] && [[scrollView verticalScroller] hitTest:aPoint] != nil)
        {
            [[scrollView verticalScroller] mouseDown:currentEvent];
        }
        // if we have a horizontal scroller and it accepts the current hit
        if([scrollView hasVerticalScroller] && [[scrollView horizontalScroller] hitTest:aPoint] != nil)
        {
            [[scrollView horizontalScroller] mouseDown:currentEvent];
        }
    }
    return [super hitTest:aPoint];
}

- (void)scrollWheel:(NSEvent *)event
{
    if(![NSEvent isSwipeTrackingFromScrollEventsEnabled])
    {
        [super scrollWheel:event];
        return;
    }
    if([event phase] == NSEventPhaseBegan)
    {
        currentSum = 0;
        NSScrollView *scrollView = [[[[webView mainFrame] frameView] documentView] enclosingScrollView];
        NSRect bounds = [[scrollView contentView] bounds];
        canGoLeft = canGoRight = NO;
        if(bounds.origin.x <= 0)
        {
            canGoLeft = YES; // && [webView canGoBack]
        }
        if(bounds.origin.x + bounds.size.width >= [[scrollView documentView] bounds].size.width)
        {
            canGoRight = YES; // && [webView canGoForward]
        }
        scrollDeltaX = 0;
        scrollDeltaY = 0;
        isHandlingEvent = canGoLeft || canGoRight;
    }
    else if([event phase] == NSEventPhaseChanged)
    {
        if(!isHandlingEvent)
        {
            if(currentSum != 0)
            {
                currentSum = 0;
                [self launchDrawTimer];
            }
        }
        else
        {
            scrollDeltaX += [event scrollingDeltaX];
            scrollDeltaY += [event scrollingDeltaY];
            
            float absoluteSumX = fabsf(scrollDeltaX);
            float absoluteSumY = fabsf(scrollDeltaY);
            if((absoluteSumX < absoluteSumY && currentSum == 0))
            {
                isHandlingEvent = NO;
                if(currentSum != 0)
                {
                    currentSum = 0;
                    [self launchDrawTimer];
                }
            }
            else
            {
                CGFloat flippedDeltaX = scrollDeltaX * -1;
                if(flippedDeltaX == 0 || (flippedDeltaX < 0 && !canGoLeft) || (flippedDeltaX > 0 && !canGoRight))
                {
                    if(currentSum != 0)
                    {
                        currentSum = 0;
                        [self launchDrawTimer];
                    }
                }
                else
                {
                    // Draw the back/forward indicators
                    currentSum = flippedDeltaX/1000;
                    [self launchDrawTimer];
                    return;
                }
            }
        }
    }
    else if([event phase] == NSEventPhaseEnded)
    {
        if(currentSum < -0.3 && canGoLeft)
        {
            NSLog(@"Go back");
            [self.webView goBack];
            
        }
        else if(currentSum >= 0.3 && canGoRight)
        {
            NSLog(@"Go forward");
            [self.webView goForward];
            
        }
        isHandlingEvent = NO;
        if(currentSum != 0)
        {
            currentSum = 0;
            [self launchDrawTimer];
        }
    }
    else if([event phase] == NSEventPhaseMayBegin || [event phase] == NSEventPhaseCancelled)
    {
        isHandlingEvent = NO;
        if(currentSum != 0)
        {
            currentSum = 0;
            [self launchDrawTimer];
        }
    }
    [super scrollWheel:event];
}

- (void)launchDrawTimer
{
    // a timer is needed because events are queued and processing and drawing
    // takes longer than they are delivered, so the queue fills up
    if(!drawTimer || ![drawTimer isValid])
    {
        self.drawTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/25 target:webView.swipeIndicator selector:@selector(display) userInfo:nil repeats:NO];
    }
}

+(void)openInfoPane{
    NSLog(@"Here we go!");
    openInfoPaneFunction();
   
}

void openInfoPaneFunction(){

    NSLog(@"Function has been activated");
    
}

-(void)openInfoPaneMethod{

    [self.webView setHidden:TRUE];
    NSLog(@"uh");
    
}

-(void)awakeFromNib{
    
    [self openInfoPaneMethod];


}

-(IBAction)showInfoPaneButton:(id)sender{
 
    if (showInfoButton.state == NSOnState) {
        NSLog(@"WebView is visible");
        [self.webView setHidden:TRUE];
    }
    
    if(showInfoButton.state == NSOffState){
    
        NSLog(@"Infopane is opened");
        [self.webView setHidden:FALSE];
        [self.webView goBack];
    
    }
    

}

@end
I decided to try and put the setHidden: code inside an IBAction in the DHSwipeClipView instead. The WebView ignores it though through the action?! The log is printed, but the WebView won't hide! If I put the code in the eventPhase region under [webView goBack]; , the webView actually hides. I then tried hiding the webView in awakeFromNib: That didnt work either

What could possibly cause this to happen?

Last edited by Blakeasd; Jun 18, 2013 at 04:54 PM.
Blakeasd is offline   0 Reply With Quote
Old Jun 19, 2013, 02:53 AM   #12
gnasher729
macrumors G5
 
gnasher729's Avatar
 
Join Date: Nov 2005
Look, class methods are there for situations where you don't have any object. For example the most important class method of all, "alloc", which you need to call when you don't have an object. You have an object, you just can't figure out where it is.

Your object is in a nib file, and there should be an IBOutlet somewhere referring to it, and you call object methods on that outlet. If it's not in a nib file, or if you don't hav a call to [[xxx alloc] init] then you don't have an object, and this isn't going to work.


Quote:
Originally Posted by Blakeasd View Post
What could possibly cause this to happen?
The answer to this is obvious: Your code.
gnasher729 is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
Resolved: No known class method for selector 'alloc' ? ArtOfWarfare Mac Programming 14 Jan 8, 2014 01:20 PM
Assigning new instance of a class to an object that had an instance of the class moonman239 iPhone/iPad Programming 3 Oct 11, 2013 12:58 PM
How to have user create class instance Abrexas iPhone/iPad Programming 21 Mar 28, 2013 12:54 PM
Taking Notes In Class with the Cornell Method? Simplicated Community Discussion 4 Oct 24, 2012 07:07 AM
Accessing a static variable from an instance method nerak99 iPhone/iPad Programming 25 Sep 13, 2012 11:26 AM

Forum Jump

All times are GMT -5. The time now is 06:15 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC