Question

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.

Was it helpful?

Solution 2

  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.

OTHER TIPS

In that code, there is no way that objc_msgSend() is incurring a measurable amount of overhead. There is far, far, too much other work going on that is potentially significantly expensive including allocations, view hierarchy manipulation, and other operations.

I.e. moving to direct function calls is a complete waste of your time.

It isn't clear why the code is failing. You would need to check the return value of methodForSelector: to make sure it makes sense. Also, note that calling an IMP in that fashion is incorrect; it needs to be typecast to the specific kind of function pointer that it truly is. I.e. void (*impPtr)(id, SEL, CGRect) ... or whatever.

Finally, making this faster is likely going to involve moving away from using so many views and into something a bit more on-demand. This appears to be a performance issue due to architecture.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top