Search code examples
objective-coptimizationfor-loopselectorimp

Objective C for loop optimization using SEL and IMP


According to this article For loops in objective C can be optimized using SEL & IMP. I have been toying with the idea for a bit now, and today I've been trying some tests. However what seems to work for one class, does not seem to work for another. Furthermore, I would like to know how exactly the speedup occurs ? By avoiding objC_mesgSent ?

Question 1 How is this:

Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        [self addSubview:cell.view];
        [self.cells addObject:cell];

Worse than this:

SEL addCellSel = @selector(addObject:);
IMP addCellImp = [self.cells methodForSelector:addCellSel];
Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        [self addSubview:cell.view];
        addCellImp(self.cells,addCellSel,cell);

Question 2 Why does this fail ? (Note self is a class that inherits from UIView)

SEL addViewSel = @selector(addSubview:);
IMP addViewImp = [self methodForSelector:addViewSel];
Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        addViewImp(self,addViewSel,cell.view);
        [self.cells addObject:cell];

The error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SimpleGridView addSubview:]: unrecognized selector sent to instance 0xaa3c400'

Tells me that method addSubview has not been found in my Class "SimpleGridView". However when I tried:

if ([self respondsToSelector:addViewSel]){
    NSLog(@"self respondsToSelector(AddViewSel)");
    addViewImp(self,addViewSel,cell.view);
} else {
    NSLog(@"self does not respond to selector (addViewSel");
   [self addSubview:cell.view];  
}

I still get the exact same error !

Question 3 Why can't I set a selector & implementation to a Class Init/new method like so:

iContactsGridCell *cell;
SEL initCellSel = @selector(initWithRect:);
IMP initCellImp = [iContactsGridCell methodForSelector:initCellSel];
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = initCellImp([iContactsGridCell new],initCellSel,tmp_frame);

For reference: Class iContactsGridCell inherits from Class Cell, which defines and implements

- (id) initWithRect:(CGRect)frame;

Furthermore, casting did not help (same error about unrecognized selector)

iContactsGridCell *cell;
SEL initCellSel = @selector(initWithRect:);
IMP initCellImp = [Cell methodForSelector:initCellSel];
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = (iContactsGridCell *)initCellImp([Cell new],initCellSel,tmp_frame);

Trying different combinations such as:

IMP initCellImp = [Cell methodForSelector:initCellSel];

Or

cell = initCellImp([iContactsGridCell class],initCellSel,tmp_frame);

Produce the exact same error. So, please tell me, what am I missing, how is this beneficial and is it possible to have an IMP/SEL for a class init method ? Also, would a C function pointer be faster compared to this, or is all the above simply an objective-C function pointer wrapper ? Thank you ! PS: I apologize if those are too many questions at once.


Solution

    1. Yes, it avoids objc_msgSend(), which (more importantly) has to performs a lookup at runtime for the right IMP based on the receiver's class and selector, even though in this case, the receiver's class and selector are the same every time, so in theory we could just look it up once and re-use it.

    2. It's hard to know what is wrong from what you've described. One possibility is that the object's class dynamically handle methods, such that it doesn't have a specific implementation for a given selector, but when called, can actually handle it (e.g. with forwardInvocation:). This is used in many places, e.g. proxy objects, or a notification object that sends things it receives to multiple targets. And such a class might say YES to respondsToSelector:, since it does respond to it. I am not sure if this is what is happening here.

    3. [iContactsGridCell methodForSelector:initCellSel] will look up a class method with that name, because you are calling methodForSelector: on iContactsGridCell, a class object. To get an instance method with that name, you would either have to call methodForSelector: on an instance of iContactsGridCell, or use the method instanceMethodForSelector: on the class.