Why use performSelector: withObject: withObject at run time if you know both the selector and its arguments at compile time?

I just stumbled upon 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]; } 

In LLVM 2.0, this results in a compilation error:

error: arithmetic on a pointer to an id interface that is not a constant size in a non-fragile ABI

I know why this error occurs, and I know how to fix it. I just need to call the method directly, for example:

  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 do you need to use performSelector:withObject:withObject: at run time? I do not understand why the code was written this way in the first place. If the selector and arguments were dynamically passed to the method, I can understand, but it’s not, the selector and its arguments are hard-coded (even if the index changes at run time, its method of obtaining the index is complex encoded.)

If someone can explain to me a good reason why this would be necessary, I would be grateful. Otherwise, I will modify all this code here.

+6
dynamic objective-c llvm runtime
source share
2 answers

After a little digging, it looks like the TTPickerTextField class in which this code is located is an indirect subclass of UITextField .

Thus, it supports the UITextField delegate function, which does not comply with the TTPickerTextFieldDelegate protocol, where the textField:didAddCellAtIndex: method is textField:didAddCellAtIndex: .

I came to the conclusion that this code is just laziness. There is no reason why the UITextField delegation UITextField should have been copied, which makes this messy, error-prone code necessary.

My own approach was to leave the UITextField delegate property separate and add my own property in my specific subclass that handled specific delegate methods.

Just to clarify - the β€œsolution” that I mentioned in the question fixes the compiler error, but generates a warning that the method cannot be found, and it is assumed that it will return id. This is what the source code "solved", but only worked in GCC. No longer with LLVM 2.0.

Last edit, I promise:

My final solution to dealing with this laziness and getting rid of warnings and errors is an ugly hack:

 [(id <TTPickerTextFieldDelegate>)self.delegate textField:self didAddCellAtIndex:(_cellViews.count - 1)]; 

Insert a UITextField delegate into an id that matches TTPickerTextFieldDelegate , and then call the method directly.

Please do not be lazy: (

+10
source share

That responsesToSelector / performSelector combo is an idiom for optional delegate methods. This method is not guaranteed to the delegate, so a direct call to it will cause a compiler warning.

What the compiler really complained 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 

represents a risky arithmetic of a pointer ... 'id' is a type of pointer, therefore:

 (id)_cellViews.count-1 

tells the compiler that it subtracts one from the pointer instead of an integer .... which is probably not the purpose of this code. The argument withObject of performSelector must be a pointer; it cannot be primitive. You can get around this by wrapping _cellViews.count - 1 in NSNumber and expanding it in the delegate method.

 [self.delegate performSelector:sel withObject:self withObject:[NSNumber numberWithInt:_cellViews.count-1]]; 
+5
source share

All Articles