I've just come across some code in Three20 that looks like this:
SEL sel = @selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];
}
On LLVM 2.0, this causes the compilation error:
error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI
I know why that error is occurring and I know how to fix it. I just need to invoke the method directly, like so:
SEL sel = @selector(textField:didAddCellAtIndex:);
if ([self.delegate respondsToSelector:sel]) {
[self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];
}
My question is, if you know both the selector and its arguments at compile time, why would you need to use performSelector:withObject:withObject:
at runtime? I don't see why the code was written this way in the first place. If the selector and arguments were dynamically passed into the method, I may understand, but they're not, the selector and its arguments are hard coded, (even if the index does change during run time, its method of obtaining the index is hard coded.)
If someone could explain to me a good reason why this would be necessary, I'd be grateful. Otherwise, I'll be over here changing all this code.
After a little more digging, it looks like the TTPickerTextField
class that this code is found in is an indirect subclass of a UITextField
.
As such, it is piggy-backing on UITextField
s delegate property, which doesn't conform to the TTPickerTextFieldDelegate
protocol where the method textField:didAddCellAtIndex:
is declared.
I have come to the conclusion that this code is just laziness. No reason why the UITextField
s delegate property had to be piggy-backed, making this confusing, error prone code necessary.
My own approach would have been to leave UITextField
s delegate property alone, and add my own property in my specific subclass that handled the specific delegate methods.
Just to clarify - the 'solution' I mentioned in the question fixes the compiler error, but generates a warning that the method can't be found and will be assumed to return id. This is what the original code was 'solving' but that only worked in GCC. No longer with LLVM 2.0.
Last edit, I promise:
My final solution to combat this laziness and get rid of the warning and error is an ugly hack:
[(id <TTPickerTextFieldDelegate>)self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)];
Cast UITextField
s delegate to an id
that conforms to TTPickerTextFieldDelegate
and then invoke the method directly.
Please don't be lazy :(
That respondsToSelector/performSelector combo is an idiom for optional delegate methods. The delegate isn't guaranteed to have that method defined, so a direct call to it would cause a compiler warning.
What the compiler was actually complaining about in this case:
[self.delegate performSelector:sel withObject:self withObject:(id)_cellViews.count-1];
error: arithmetic on pointer to interface 'id', which is not a constant size in non-fragile ABI
is risky pointer arithmetic... 'id' is a pointer type, so:
(id)_cellViews.count-1
tells the compiler it's going to subtract one from a pointer instead of an integer....which is probably not the intent of that code. The withObject argument of performSelector has to be a pointer, it can't be a primitive. You can get around this by wrapping _cellViews.count - 1 in an NSNumber, and unwrapping it in the delegate method.
[self.delegate performSelector:sel withObject:self withObject:[NSNumber numberWithInt:_cellViews.count-1]];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With