代码之家  ›  专栏  ›  技术社区  ›  TechZen

在类别中实现时无法识别协议方法

  •  3
  • TechZen  · 技术社区  · 15 年前

    我有一个视图控制器类,它必须实现几个协议。保持整洁我习惯于将每个协议的方法放在视图控制器类的一个类别中。

    这一次,链接器警告我类没有实现其中一个协议。方法在运行时确实有效,链接器似乎无法识别类别中的实现。

    我在一个不同的项目中简化了类,在同一个地方得到了相同的错误。

    类标题:

    #import <UIKit/UIKit.h>
    #import <AddressBook/AddressBook.h>
    #import <AddressBookUI/AddressBookUI.h>
    
    @interface TopVC : UIViewController 
    <
        UINavigationControllerDelegate,
        ABPeoplePickerNavigationControllerDelegate  
    >
    {}
    @end
    

    topvc.m(未显示)是自动生成的,没有任何更改。这个 UINavigationControllerDelegate 协议方法实现在以下类别中:

    #import <Foundation/Foundation.h>
    #import "TopVC.h"
    
    @interface TopVC (UINavigationControllerDelegate)
    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
    - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;    
    @end
    
    #import "TopVC+UINavigationControllerDelegate.h"
    
    @implementation TopVC (UINavigationControllerDelegate)
    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
        NSLog(@"navigationController:willShowViewController:animated:");
    }
    
    - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
        NSLog(@"navigationController:didShowViewController:animated:");
    }
    @end
    

    链接器不会抱怨此类别中的此方法。但是,如果我尝试一个类别来实现 ABPeoplePickerNavigationControllerDelegate 协议以同样的方式抱怨:

    #import "TopVC.h"
    
    @interface TopVC (ABPeoplePickerNavigationControllerDelegate)
    
    - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker;
    
    - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person;
    
    - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;
    
    @end
    
    #import "TopVC+ABPeoplePickerNavigationControllerDelegate.h"
    
    
    @implementation TopVC (ABPeoplePickerNavigationControllerDelegate)
    
    - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
    
    }
    
    - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
        return YES;
    }
    
    - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
        return YES;
    }
    @end
    

    链接器抱怨:

    warning: incomplete implementation of class 'TopVC'
    warning: method definition for '-peoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier:' not found
    warning: method definition for '-peoplePickerNavigationController:shouldContinueAfterSelectingPerson:' not found
    warning: method definition for '-peoplePickerNavigationControllerDidCancel:' not found
    warning: class 'TopVC' does not fully implement the 'ABPeoplePickerNavigationControllerDelegate' protocol
    

    我能看到的唯一区别是 飞机操纵员 协议方法都是可选的,而 abpeoplepickernavigationcontrollerdelegate 都是必需的。

    然而,即使链接器抱怨,这些方法仍然在运行时被调用。我只是拒绝建造一个有警告的建筑。我显然漏掉了什么东西,或者在某个地方犯了一个小错误,但我看不出来。

    3 回复  |  直到 15 年前
        1
  •  6
  •   TechZen    15 年前

    哈!我脑子里一片空白。

    我忘记将协议实现声明移动到类别的接口,如下所示:

    #import "TopVC.h"
    
    @interface TopVC (ABPeoplePickerNavigationControllerDelegateMethods) <ABPeoplePickerNavigationControllerDelegate>
    ...
    @end
    

    这将在没有警告的情况下编译并按预期工作。

    我只是懒得使用这么多可选协议,如果链接器找不到方法实现,它就会忽略这些协议。

        2
  •  2
  •   Dirk    15 年前

    通过声明协议,您打算在主接口上实现,编译器希望看到主实现中的方法(关于您声明的方法)。[它]是自动生成的,没有任何更改)。编译器不知道,您将在一个类别中实现这些方法。它只知道:你“承诺”他们会在那里,但你没有提供他们的地方,它期待他们。

        3
  •  0
  •   Rob Napier    15 年前

    当编译器编译topvc.m时,它无法知道其他文件将提供所需的其余协议方法。在gcc中,每个.m文件都是独立编译的。你想把这些东西分成不同的类别是可以的,但是所有这些都需要在topvc.m中实现。

    你对协议方法的重新定义 TopVC+ABPeoplePickerNavigationControllerDelegate.h 对任何事情都没有影响。@interface中的特定category标记与@implementation中的特定category标记之间没有关系。@interface中的一个类别定义只是说“这里有一些其他方法可以合法地传递给这个对象,而不生成编译器警告。”甚至连创建这样的方法的明确承诺都没有。当您表明您符合协议(这是实现方法的承诺)时,已经处理了这个问题。

    @implementation中的category定义只是说“这个类还有其他一些方法”,category标记本身只是一个注释。它没有任何关联。

    您需要将协议实现移到topvc.m中,并且没有理由 TopVC+<protocol>.h 文件。如果你想把你的.m分解成单独的@implementation块,没关系,我知道有人会这样做。就我个人而言,我只是 #pragma mark 把文件拆开。