views:

403

answers:

5

I have a method that returns an NSRange. When I call this method from outside the class I get a compile error.

NSRange tmpRange;
tmpRange = [phrase rangeInString:searchString forString:theLetter goingForward:YES];
return tmpRange.location == -1;

in the .h file:

#import <Foundation/Foundation.h>


@interface Phrase : NSObject {

}
- (NSRange) rangeInString:(NSString *) tgt forString:(NSString *) find goingForward:(BOOL) fwd;  

@end

This method is called within the Phrase object by other methods without problems. The compiler says 'incompatible types in assignment'.

Can anyone explain this to me? I assume it has to do with returning an NSRange/struct type value generated outside the object, but I don't know why it works in one place and not the other.

+5  A: 

Of course a method can return NSRange. But returning structures require special attention to the compiler because how the method is invoked is usually different (objc_msgSend_stret vs. objc_msgSend).

Please make sure you declare phrase as

Phrase* phrase = ...;

so that the compiler knows -rangeInString:… is returning an NSRange, instead of

id phrase = ...;

(Also, since you don't show which line the compiler errors, make sure the function using return … == -1; is returning a BOOL not NSRange.)

KennyTM
You also need to make certain that the header file containing the Phrase interface is imported into the file that invokes the method.
Matt Gallagher
Phrase is imported -- see my comment below. The instance of Phrase is already declared, and multiple references are made to the Phrase instance elsewhere in the same class, in other methods.The line that throws the error is the one that assigns tmpRange to the result of the method call.
Dan Donaldson
@Dan: How do you declare the **variable** `phrase`?
KennyTM
see my answer added below
Dan Donaldson
+1  A: 

Can a method return an NSRange?

Yes.

This method is called within the Phrase object by other methods without problems. … When I call this method from outside the class I get a compile error. … The compiler says 'incompatible types in assignment'.

Remember to #import Phrase.h into the implementation file where you're talking to a Phrase object. Otherwise, the compiler doesn't know about your rangeOfString:forString:goingForward: method and assumes that it returns id. It needs the @interface in the header file to know that the method returns NSRange.

Assuming that your phrase object is in an instance variable, you probably declared the Phrase class with @class Phrase; in the header file where you declared the ivar. That's a forward declaration; it tells the compiler that a class named “Phrase” exists (so that a declaration like Phrase *myPhrase will be legal), but it doesn't tell the compiler anything else about it. To do that, you need to import the header.

The general rule, for instances of class A knowing about instances of class B, is for A's header (A.h) to forward-declare class B with @class, and A's implementation (A.m) to import B's header (B.h).

A different case is when A is a subclass of B. You need the @interface to make a subclass, so in this case and this case only, A.h must import B.h. Since it's importing the header for B, it does not need to forward-declare B.

Peter Hosey
Actually, the Phrase class is imported, not declared with @class. The non-Phrase class imports Phrase, and Phrase does not import the other class (just Foundation). Phrase declares that the return value is an NSRange. The class calling the Phrase-class method stores an instance of Phrase, called phrase inside itself.
Dan Donaldson
+1  A: 

I don't think this is the answer, but NSRange.location is declared as NSUInteger. By comparing it to -1, you are comparing an unsigned value to a signed value.

The only other answer I can think of is that the .m file you are making the call from has not imported the header Phrase.h. Now, I know you believe it has, but the compiler is saying otherwise. Try running the preprocessor on the file to see what it is actually importing. (right click in an editor containing the .m file - preprocess is near the bottom of the context menu that pops up).

JeremyP
A: 

Yes, you can return C structs in methods. That is not the problem. Also, location is an NSUInteger and will never be -1. Use NSNotFound to test for that.

How is the instance of your Phrase object typed? Did you use id instead of Phrase *? Have you properly imported the Phrase header?

You should give us more information about the code that's giving the compile error.

Preston
A: 

In response to some questions that are coming up:

#import <Foundation/Foundation.h>
#import "OverlayView.h"
#import "Phrase.h"
#import "TimerPaneView.h"

Is the header in the MainPane.h, the class header for where the call is being made.

In the header for MainPane.h, phrase is declared:

    Phrase              *phrase;

And in the implementation file, which, yes, imports the MainPane header:

phrase = [[Phrase alloc] init];

Is how the phrase class is being instantiated, in the initWithFrame method. I refer to the phrase object dozens and dozens of times, calling methods, referring to properties in this class, without problems. And no, the phrase object is never released until dealloc.

-- added: I thought I had found the problem: a collapsed section of code that never is called that had a legacy return value that was BOOL. This method originally returned BOOL, but was converted. But I eliminated it, and the problem remains.

I should also say that I've refactored my way around this problem, but it remains a mystery...

Dan Donaldson