0

根据这篇文章,目标 C 中的 For 循环可以使用 SEL 和 IMP 进行优化。我一直在玩弄这个想法,今天我一直在尝试一些测试。然而,似乎对一个班级有效,似乎对另一个班级无效。此外,我想知道加速究竟是如何发生的?通过避免 objC_mesgSent

问题 1 这是怎么回事:

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

比这更糟糕:

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

问题 2 为什么会失败?(注意self是继承自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];

错误:

由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[SimpleGridView addSubview:]:无法识别的选择器发送到实例 0xaa3c400”

告诉我在我的“SimpleGridView”类中找不到方法addSubview。但是,当我尝试时:

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

我仍然得到完全相同的错误!

问题 3 为什么我不能像这样将选择器和实现设置为 Class Init/new 方法:

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

供参考:类iContactsGridCell继承自 Class Cell,它定义和实现

- (id) initWithRect:(CGRect)frame;

此外,强制转换没有帮助(关于无法识别的选择器的相同错误)

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

尝试不同的组合,例如:

IMP initCellImp = [Cell methodForSelector:initCellSel];

或者

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

产生完全相同的错误。所以,请告诉我,我错过了什么,这有什么好处,是否有可能为类 init 方法提供 IMP/SEL?此外,与此相比,C 函数指针会更快,还是以上所有内容都只是一个目标 C 函数指针包装器?谢谢 !PS:如果这些问题一次太多,我深表歉意。

4

2 回答 2

4

在该代码中,没有办法objc_msgSend()产生可衡量的开销。有很多其他工作正在进行,可能非常昂贵,包括分配、视图层次结构操作和其他操作。

即转向直接函数调用完全是在浪费您的时间。

目前尚不清楚代码失败的原因。您需要检查 的返回值methodForSelector:以确保它有意义。另外,请注意以这种方式调用 IMP 是不正确的;它需要被类型转换为它真正是的特定类型的函数指针。即void (*impPtr)(id, SEL, CGRect) ...或什么的。

最后,加快这一进程可能会涉及从使用如此多的视图转向更多按需使用的东西。由于架构,这似乎是一个性能问题。

于 2012-12-21T21:50:02.620 回答
2
  1. 是的,它避免了objc_msgSend(),它(更重要的是)必须在运行时根据接收者的类和选择器执行正确的 IMP 查找,即使在这种情况下,接收者的类和选择器每次都是相同的,所以理论上我们可以只查找一次并重新使用它。

  2. 从你描述的内容很难知道什么是错的。一种可能性是对象的类动态处理方法,这样它就没有给定选择器的特定实现,但是当被调用时,实际上可以处理它(例如使用forwardInvocation:)。这在很多地方都有使用,例如代理对象,或者将接收到的东西发送到多个目标的通知对象。这样的类可能会说YESto respondsToSelector:,因为它确实响应它。我不确定这是否是这里发生的事情。

  3. [iContactsGridCell methodForSelector:initCellSel]将查找具有该名称的类方法,因为您正在调用methodForSelector:一个iContactsGridCell对象。要获得具有该名称的实例方法,您要么必须调用methodForSelector:的实例iContactsGridCell,要么使用instanceMethodForSelector:类上的方法。

于 2012-12-22T00:28:23.617 回答