3

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

这次我从链接器收到警告说该类没有实现协议之一。这些方法在运行时确实有效,链接器似乎无法识别类别中的实现。

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

类头:

#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

我能看到的唯一区别是UINavigationControllerDelegate协议方法都是可选的,而ABPeoplePickerNavigationControllerDelegate都是必需的。

然而,即使链接器抱怨,这些方法仍然在运行时被调用。我只是拒绝构建带有警告的构建。我显然错过了一些东西或在某处犯了一个微不足道的错误,但我无法发现它。

4

3 回答 3

6

哈!我脑子一片空白。

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

#import "TopVC.h"

@interface TopVC (ABPeoplePickerNavigationControllerDelegateMethods) <ABPeoplePickerNavigationControllerDelegate>
...
@end

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

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

于 2010-03-06T02:02:24.837 回答
2

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

于 2010-03-06T00:34:07.393 回答
0

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

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

@implementation 中的类别定义只是说“这里是这个类的一些其他方法”。类别标签本身只不过是一条评论。它不以任何方式相关。

您需要将协议实现移动到 TopVC.m 中,并且没有理由拥有一个TopVC+<protocol>.h文件。如果您想将 .m 分解为单独的 @implementation 块,那很好,我知道有人这样做。就个人而言,我只是#pragma mark用来分解文件。

于 2010-03-06T00:47:53.943 回答