Objective-C isEmpty helper suddenly stopping build - ios

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

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.

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 ] ;

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.

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.

Related

Dart, overriding not equal operator gives error

I've been developing dart(flutter) for some while and I came upon this Error. I was creating a custom class that had several operator overrides. For explanation purposes, the class looks like this.
class CustomObject {
int big;
int small;
CustomObject(this.big, this.small);
#override
bool operator ==(Object other) {
if (other is CustomObject) {
return big == other.big && small == other.small;
}
return false;
}
#override
int get hashCode => big.hashCode ^ small.hashCode;
#override
bool operator !=(Object other) {
// Error: The string '!=' isn't user-definable operator.
if (other is CustomObject) {
return big != other.big || small != other.small;
}
return false;
}
}
The error occurs on the != operator override. Looking at this website https://www.geeksforgeeks.org/operator-overloading-in-dart/ it says that you can override != operator. I could not find any other source that documents overriding this operator.
My question is, 1. are you supposed to override != in the first place? 2. if so, is are any restrictions on overriding != operators?

How does one end a function in dart similar to in JS typing return

In Javascript when I want to end a function I usually write return, but in the dart, if I do that then the function asks for a return statement at the end of each branch which is messy.
Function createCategory(File image) {
String title = _categoryTitle.value;
if(image == null || title == null || title.length == 0) {
return null;
}
CategoryModel newCategory = new CategoryModel();
if(_categories.value == null) {
_categories.sink.add([newCategory]);
}
return null;
}
What is the correct way to do this in dart?
I think your confusion comes from that your method is valid Dart code but does not really do what I guess you think. Your method has the following signature:
Function createCategory(File image) {
This means your method is named createCategory, takes one argument of the type File and returns a Function. Since you have marked the method to return a Function, then Dart will tell you it is a problem if you just return; since this is properly not what you wanted.
If your method is not going to return any value, then you want to mark this with the return type of void like:
void createCategory(File image) {
String title = _categoryTitle.value;
if (image == null || title == null || title.length == 0) {
return;
}
CategoryModel newCategory = new CategoryModel();
if (_categories.value == null) {
_categories.sink.add([newCategory]);
}
return;
}
I should note, that the last return can be skipped since Dart will automatically add return; at the end of a function if it is missing.
you can do that like this:
void createCategory(File image) {
String title = _categoryTitle.value;
if (image != null && title.isNotEmpty && _categories.value == null) {
_categories.sink.add([CategoryModel()]);
}
}

Xcode complains about Unused functions that are used

I have a "MyConstants.h" file that is imported by several classes.
Inside that file I have things like:
static BOOL isIndexValid(NSInteger index) {
return ((index >=0) && (index < 200));
}
This function is extensively used by the classes importing MyConstants.h. Even so, Xcode complains that this function and others are not used.
Why?
Defining a static function (or variable, for that matter) in a header file means every source file that imports that header file will get its own copy.
That is not good and is what the compiler is complaining about (not every source file references this function).
Make it static inline instead:
static inline BOOL isIndexValid(NSInteger index) {
return ((index >=0) && (index < 200));
}
Try to insert __unused between return type and function name, and it works for me on Xcode 10.2
static BOOL __unused isIndexValid(NSInteger index) {
return ((index >=0) && (index < 200));
}
Hope it will be helpful for you.

Accessing utility methods from any class instance?

(This has probably been answered elsewhere but I don't know what to search on to find it)
I have the printErrorMessage method below which I find very useful. I've been including it in all my classes but that is kind of dumb in terms of duplicating code. Can I just define this as a Class method in a separate Utility class?
This is on iOS, if that matters.
- (void) printErrorMessage: (NSString *) errorString withStatus: (OSStatus) result
{
char str[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(result);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
str[0] = str[5] = '\'';
str[6] = '\0';
} else
// no, format it as an integer
sprintf(str, "%d", (int)result);
NSLog (#"*** %# error: %s\n", errorString, str);
}
Can I just define this as a Class method in a separate Utility class?
Of course you can. That is what class methods are for. Alternatively, inject it into some existing class by way of a category.
I use to create a class with static methods:
#interface Utils
+ (void) printErrorMessage: (NSString *) errorString withStatus: (OSStatus) result
#end
Now you can call this from anywhere in your code using:
[Utils printErrorMessage:#"string" withStatus:status];

Expect Argument Type to be integer but getting id instead

I'm using the forwardInvocation: feature of objective-c and I need to know what type of argument the method received. In my example I'm passing it an int but getArgumentTypeAtIndex: tells me it's an id instead. Here's a simple example:
#interface Do : NSObject
+ (void) stuff:(int)x;
#end
#implementation Do
+ (NSMethodSignature *) methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature)
signature = [self methodSignatureForSelector:#selector(forwardInvocation:)];
return signature;
}
+ (void)forwardInvocation:(NSInvocation *)i
{
const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
NSLog(#"%s == %s", argType, #encode(id)); // # == #
NSLog(#"%s == %s", argType, #encode(int)); // # == i
}
#end
Here's how I call it:
[Do stuff:123];
Any idea why I'm not getting id instead of int as the type?
The problem is that you don't have actually have a stuff: method on the class so methodSignatureForSelector: will return nil - it looks like you discovered that and so implemented your own version, but that fails on the super call and so ends up returning the signature of forwardInvocation: - which is not what you want!
To get around this you either need to direct the methodSignatureForSelector: to a class which has the selector, or use a protocol - if a class implements a protocol then it will return the signature for any methods in that protocol even if the methods are not actually implemented by that class.
Here is your sample using a protocol:
#protocol DoProtocol
#optional
+ (void) stuff:(int)x;
#end
#interface Do : NSObject<DoProtocol>
#end
#implementation Do
+ (void)forwardInvocation:(NSInvocation *)i
{
const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
NSLog(#"%s == %s", argType, #encode(id)); // # == #
NSLog(#"%s == %s", argType, #encode(int)); // # == i
}
#end
The #optional avoids any compiler warnings for unimplemented methods. The default implementation of methodSignatureForSelector: (from NSObject) will return a valid signature obtained from the protocol, and so forwardInvocation: will be called.
As long as you can get it past the compiler, whatever you pass as an argument will be interpreted as such at runtime - you could declare that a function takes an NSNumber, but it you pass a UITableView to it, it's class will still be a UITableView.

Resources