5

I've got this beautiful & convenient helper inline function that i have in a project (originally has it's roots in here & here):

static inline BOOL isEmpty(id thing) {
    return !thing
    || [thing isKindOfClass:[NSNull class]]
    || ([thing respondsToSelector:@selector(length)] && [((id)thing) length] == 0)
    || ([thing respondsToSelector:@selector(count)] && [((id)thing) count] == 0);
}

static inline BOOL isNotEmpty(id thing) {
    return !isEmpty(thing);
}

and all works well.

it's useful for checking NSString, NSData, NSArray, NSDictionary, NSSet, and others... My issue now is that I brought it in to another project(a static framework/library that i'll be using) and have the following issue that is stopping my project from building:

Multiple methods named 'count' found with mismatched result, parameter type or attributes

I'm using the same(latest) version of xCode with both so not sure what the difference could be that would stop this on one side and not the other... The project settings are obviously different in either project (as mentioned, one is a framework and one is a regular project) but would that do it?

thanks in advance!


POST-SOLUTION-EDIT for future visits:

hold command and click on the method or property to get a drop down of all the instances that the compiler is seeing... you likely have conflicting return types.

Community
  • 1
  • 1
MrTristan
  • 739
  • 5
  • 17
  • 1
    Why do you call it a macro? Its an inline function? – paulm Dec 19 '13 at 20:20
  • changed it over for correctness. i realize, but mentally i more or less equate the two... :-) – MrTristan Dec 19 '13 at 20:22
  • 1
    macros can have lots of whacky side effects, and break if not put inside a fake loop to scope them etc – paulm Dec 19 '13 at 20:24
  • curious why you want to use `isEmpty`? Seems like a case where `nil` can be your empty value. Do you not know the expected type of the object? Otherwise if you want to check if an array (for example) is `nil` or an empty array, just use `array.count == 0`. This will handle a `nil` array and an empty one, etc. – nielsbot Dec 20 '13 at 19:06
  • consistently checking for NSNull is also an issue for me. i do a lot of JSON parsing and a lot of communication with server endpoints... this function comes in to a lot of play when checking fields or checking variables after pulling from the json and before playing with them. personally, its a nice level of abstraction to have something like this. – MrTristan Dec 20 '13 at 19:31

4 Answers4

3

It sounds like the problem is that some class(es) in the framework/library declares a -count method that returns something different than -[NSArray count] (etc.).

Even when you're sending a message to an object of unknown (id) type, the compiler needs to know some information about the method that will be called, including the return type. In short, this is because the message send path is different depending on the return type of the method that will be called. In cases like your code, the compiler will look for any method declared in the project with a name matching the message you're sending, and assume that's the method that will be called for the purposes of figuring out return type, etc. In this case, it's finding two different methods with the same name, but which have differing return types, and therefore doesn't know the exact semantics required for sending the count message.

The quick and dirty solution is to change the cast in your isEmpty() function to [(NSArray *)thing count]. This should work fine as long as you never call isEmpty() with instances of whatever class it is that has a different -count method.

jlehr
  • 15,557
  • 5
  • 43
  • 45
Andrew Madsen
  • 21,309
  • 5
  • 56
  • 97
  • yea i realize that but the beauty of this thing is that it is agnostic as to what data type you're using in that case... i could have multiple conditions for each but would much rather not... the bigger question is why does it work in an application that i have live in the app store but not in this framework that i'm building? – MrTristan Dec 19 '13 at 20:24
  • 1
    Right, but casting to `NSArray` doesn't mean the `thing` has to *be* an NSArray. It just tells the compiler to treat it as one and assume that the `-count` method that will be called will match `NSArray`'s count method. This `isEmpty()` function relies on the fact that for the normal collection classes (`NSArray`, `NSSet`, `NSDictionary`, etc.), `-count` is declared the same way (`-(NSUInteger)count;`). – Andrew Madsen Dec 19 '13 at 20:28
  • so you're saying that i could cast it to an NSArray and not worry about passing in an NSDictionary or an NSSet? intersting – MrTristan Dec 19 '13 at 20:29
  • Yes. Keep in mind that it *won't* be safe to call `isEmpty(someFrameworkObject)` where `someFrameworkObject` is an instance of whatever class(es) in the framework have a different kind of `-count` method. – Andrew Madsen Dec 19 '13 at 20:30
  • of course. so i just found the conflicting 'count'... it's an (int) property of a class of mine. i'm going to mark you as the correct answer for your help, i appreciate it. for visibility's sake for future visits, please make a note of the following in your answer: hold command and click on the property name that has the error to get a drop down of all of the definitions that the compiler is seeing, which in turn seriously helps in tracking down the conflict. – MrTristan Dec 19 '13 at 20:37
  • although i am still confused as to why this framework doesn't compile and the project does... – MrTristan Dec 19 '13 at 20:38
1

update: changed the sense of the method to is not empty, to handle nil values too.

Not quite an answer, but you could do this with categories:

@interface NSObject (IsEmpty)
-(BOOL)isNotEmpty ;
@end

@implementation NSObject (IsEmpty)
-(BOOL)isNotEmpty { return NO ; /* I guess? */ }
@end

@implementation NSArray (IsEmpty)
-(BOOL)isNotEmpty { return self.count > 0 ; }
@end

@implementation NSDictionary (IsEmpty)
-(BOOL)isNotEmpty { return self.count > 0 ; }
@end

@implementation NSSet (IsEmpty)
-(BOOL)isNotEmpty { return self.count > 0 ; }
@end

@implementation NSNull (IsEmpty)
-(BOOL)isNotEmpty { return NO ; }
@end

Now you can do this:

id objectOrNil = ... ;
BOOL isEmpty = ![ objectOrNil isNotEmpty ] ;
nielsbot
  • 15,922
  • 4
  • 48
  • 73
0

I guess the proper way would be to explicitly cast the result of [thing count] to integer:

static inline BOOL isEmpty(id thing) {
    return !thing
    || [thing isKindOfClass:[NSNull class]]
    || ([thing respondsToSelector:@selector(length)] && [((id)thing) length] == 0)
    || ([thing respondsToSelector:@selector(count)] && (NSUInteger)[((id)thing) count] == 0);
}

This will tell compiler that you expect to see unsigned integer as a result of this method invocation.

kovpas
  • 9,553
  • 6
  • 40
  • 44
0
static inline BOOL isEmpty(id thing) {
return !thing
|| [thing isKindOfClass:[NSNull class]]
|| ([thing respondsToSelector:@selector(length)]
    && [(NSData *) thing length] == 0)
|| ([thing respondsToSelector:@selector(count)]
    && [(NSArray *) thing count] == 0);

}

It shouldn't confuse with [NSArray count] method. Hope that will help.

chathuram
  • 616
  • 2
  • 5
  • 23
  • yea issue here then is that this will only work for NSData & NSArray and not all the other classes that i pass in to it – MrTristan Dec 19 '13 at 20:35
  • 1
    Not only NSArray, you can use for other data structures as well like @Andrew Madsen mentioned above. – chathuram Dec 19 '13 at 20:42