概述
在 Objective-C 开发中,协议(Protocol)是实现接口抽象和多态的重要机制。然而,编译器对协议实现的检查有时存在局限性,特别是在动态运行时和复杂的继承关系中。本文将介绍一个完整的协议一致性检查解决方案,涵盖基础实现、功能扩展。
完整代码
Objective-c
// ProtocolConformanceChecker.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ProtocolConformanceChecker : NSObject
/**
验证对象是否完整实现了指定协议
@param objc 要验证的对象
@param protocol 要验证的协议
@param checkOptionalMethods 是否检查可选方法
@param checkClassMethods 是否检查类方法
*/
+ (void)assertObjC:(id)objc
conformsToProtocol:(Protocol *)protocol
checkOptionalMethods:(BOOL)checkOptionalMethods
checkClassMethods:(BOOL)checkClassMethods;
@end
NS_ASSUME_NONNULL_END
// ProtocolConformanceChecker.m
#import "ProtocolConformanceChecker.h"
#import <objc/runtime.h>
@interface _ProtocolMethodInfo : NSObject
@property (nonatomic, copy) NSString *methodName;
@property (nonatomic, copy) NSString *typeEncoding;
@property (nonatomic, assign) BOOL isRequired;
@property (nonatomic, assign) BOOL isInstanceMethod;
@end
@implementation _ProtocolMethodInfo
@end
@implementation ProtocolConformanceChecker
#pragma mark - 主验证方法
+ (void)assertObjC:(id)objc
conformsToProtocol:(Protocol *)protocol
checkOptionalMethods:(BOOL)checkOptionalMethods
checkClassMethods:(BOOL)checkClassMethods {
// 1. 获取所有需要检查的方法
NSArray<_ProtocolMethodInfo *> *allMethods =
[self getAllMethodsForProtocol:protocol
checkOptionalMethods:checkOptionalMethods
checkClassMethods:checkClassMethods];
// 2. 验证每个方法的实现
NSMutableArray<NSString *> *unconformsMethods = [NSMutableArray array];
for (_ProtocolMethodInfo *methodInfo in allMethods) {
if (![self object:objc implementsMethod:methodInfo]) {
NSString *methodDesc = [self formatMethodDescription:methodInfo];
[unconformsMethods addObject:methodDesc];
}
}
// 3. 报告验证结果
[self reportValidationResultForObject:objc
unconformsMethods:unconformsMethods];
}
#pragma mark - 私有辅助方法
+ (NSArray<_ProtocolMethodInfo *> *)getAllMethodsForProtocol:(Protocol *)protocol
checkOptionalMethods:(BOOL)checkOptionalMethods
checkClassMethods:(BOOL)checkClassMethods {
NSMutableArray<_ProtocolMethodInfo *> *allMethods = [NSMutableArray array];
// 获取必需方法
[allMethods addObjectsFromArray:
[self getMethodsForProtocol:protocol
isRequired:YES
checkClassMethods:checkClassMethods]];
// 获取可选方法(如果需要)
if (checkOptionalMethods) {
[allMethods addObjectsFromArray:
[self getMethodsForProtocol:protocol
isRequired:NO
checkClassMethods:checkClassMethods]];
}
return [allMethods copy];
}
+ (NSArray<_ProtocolMethodInfo *> *)getMethodsForProtocol:(Protocol *)protocol
isRequired:(BOOL)isRequired
checkClassMethods:(BOOL)checkClassMethods {
NSMutableArray<_ProtocolMethodInfo *> *methods = [NSMutableArray array];
// 获取当前协议的方法
[methods addObjectsFromArray:
[self getMethodsForSingleProtocol:protocol
isRequired:isRequired
checkClassMethods:checkClassMethods]];
// 递归获取继承协议的方法
unsigned int protocolListCount;
Protocol * __unsafe_unretained _Nonnull * _Nullable protocols =
protocol_copyProtocolList(protocol, &protocolListCount);
for (unsigned int i = 0; i < protocolListCount; i++) {
[methods addObjectsFromArray:
[self getMethodsForProtocol:protocols[i]
isRequired:isRequired
checkClassMethods:checkClassMethods]];
}
if (protocols) free(protocols);
return [methods copy];
}
+ (NSArray<_ProtocolMethodInfo *> *)getMethodsForSingleProtocol:(Protocol *)protocol
isRequired:(BOOL)isRequired
checkClassMethods:(BOOL)checkClassMethods {
NSMutableArray<_ProtocolMethodInfo *> *methods = [NSMutableArray array];
// 检查实例方法
unsigned int instanceMethodCount;
struct objc_method_description *instanceMethodDescriptions =
protocol_copyMethodDescriptionList(protocol,
isRequired,
YES, // 实例方法
&instanceMethodCount);
for (unsigned int i = 0; i < instanceMethodCount; i++) {
_ProtocolMethodInfo *info = [_ProtocolMethodInfo new];
info.methodName = NSStringFromSelector(instanceMethodDescriptions[i].name);
info.typeEncoding = [NSString stringWithUTF8String:instanceMethodDescriptions[i].types];
info.isRequired = isRequired;
info.isInstanceMethod = YES;
[methods addObject:info];
}
if (instanceMethodDescriptions) free(instanceMethodDescriptions);
// 检查类方法(如果需要)
if (checkClassMethods) {
unsigned int classMethodCount;
struct objc_method_description *classMethodDescriptions =
protocol_copyMethodDescriptionList(protocol,
isRequired,
NO, // 类方法
&classMethodCount);
for (unsigned int i = 0; i < classMethodCount; i++) {
_ProtocolMethodInfo *info = [_ProtocolMethodInfo new];
info.methodName = NSStringFromSelector(classMethodDescriptions[i].name);
info.typeEncoding = [NSString stringWithUTF8String:classMethodDescriptions[i].types];
info.isRequired = isRequired;
info.isInstanceMethod = NO;
[methods addObject:info];
}
if (classMethodDescriptions) free(classMethodDescriptions);
}
return [methods copy];
}
+ (BOOL)object:(id)objc implementsMethod:(_ProtocolMethodInfo *)methodInfo {
if (methodInfo.isInstanceMethod) {
// 检查实例方法
Method method = class_getInstanceMethod([objc class],
NSSelectorFromString(methodInfo.methodName));
if (!method) return NO;
// 检查方法签名是否匹配
const char *typeEncoding = method_getTypeEncoding(method);
return strcmp(typeEncoding, methodInfo.typeEncoding.UTF8String) == 0;
} else {
// 检查类方法
Method method = class_getClassMethod([objc class],
NSSelectorFromString(methodInfo.methodName));
if (!method) return NO;
// 检查方法签名是否匹配
const char *typeEncoding = method_getTypeEncoding(method);
return strcmp(typeEncoding, methodInfo.typeEncoding.UTF8String) == 0;
}
}
+ (NSString *)formatMethodDescription:(_ProtocolMethodInfo *)methodInfo {
NSString *methodType = methodInfo.isInstanceMethod ? @"实例方法" : @"类方法";
NSString *requirement = methodInfo.isRequired ? @"必需" : @"可选";
return [NSString stringWithFormat:@"%@ [%@, %@]",
methodInfo.methodName,
methodType,
requirement];
}
+ (void)reportValidationResultForObject:(id)objc
unconformsMethods:(NSArray<NSString *> *)unconformsMethods {
if (unconformsMethods.count == 0) {
return; // 验证通过
}
NSString *errorMessage = [NSString stringWithFormat:
@"%@ 未实现以下方法:\n%@",
objc,
[unconformsMethods componentsJoinedByString:@"\n"]];
// 使用断言,在调试时中断执行
NSAssert(NO, @"%@", errorMessage);
// 生产环境记录日志
#ifdef RELEASE
NSLog(@"Protocol Conformance Error: %@", errorMessage);
#endif
}
@end
流程图

核心功能特性
1. 完整的协议继承链检查
系统采用递归算法遍历协议的所有父协议,确保检查完整的继承关系:
ini
// 递归获取继承协议的方法
unsigned int protocolListCount;
Protocol **protocols = protocol_copyProtocolList(protocol, &protocolListCount);
for (unsigned int i = 0; i < protocolListCount; i++) {
[self getMethodsForProtocol:protocols[i]
isRequired:isRequired
checkClassMethods:checkClassMethods];
}
2. 灵活的方法检查配置
支持四种检查模式的任意组合:
objectivec
// 使用示例 - 完整检查
[ProtocolConformanceChecker assertObjC:myObject
conformsToProtocol:@protocol(MyProtocol)
checkOptionalMethods:YES // 检查可选方法
checkClassMethods:YES]; // 检查类方法
// 使用示例 - 最小检查
[ProtocolConformanceChecker assertObjC:myObject
conformsToProtocol:@protocol(MyProtocol)
checkOptionalMethods:NO // 不检查可选方法
checkClassMethods:NO]; // 不检查类方法
3. 详细的方法签名验证
不仅检查方法是否存在,还验证方法签名(Type Encoding)是否完全匹配:
scss
+ (BOOL)object:(id)objc implementsMethod:(_ProtocolMethodInfo *)methodInfo {
const char *typeEncoding = method_getTypeEncoding(method);
return strcmp(typeEncoding, methodInfo.typeEncoding.UTF8String) == 0;
}
实现细节解析
方法信息封装
使用轻量级的内部类封装方法信息,提高代码的可读性和可维护性:
less
@interface _ProtocolMethodInfo : NSObject
@property (nonatomic, copy) NSString *methodName; // 方法名
@property (nonatomic, copy) NSString *typeEncoding; // 类型编码
@property (nonatomic, assign) BOOL isRequired; // 是否必需
@property (nonatomic, assign) BOOL isInstanceMethod; // 是否为实例方法
@end
内存管理规范
严格遵守 Objective-C 运行时内存管理规范:
scss
// 正确释放运行时分配的内存
if (instanceMethodDescriptions) free(instanceMethodDescriptions);
if (protocols) free(protocols);
清晰的错误报告
提供详细的错误信息,快速定位问题:
ini
+ (NSString *)formatMethodDescription:(_ProtocolMethodInfo *)methodInfo {
NSString *methodType = methodInfo.isInstanceMethod ? @"实例方法" : @"类方法";
NSString *requirement = methodInfo.isRequired ? @"必需" : @"可选";
return [NSString stringWithFormat:@"%@ [%@, %@]",
methodInfo.methodName,
methodType,
requirement];
}
执行流程详解
步骤1:方法收集阶段

步骤2:方法验证阶段

步骤3:结果报告阶段

使用场景示例
场景一:单元测试中的协议验证
objectivec
// 验证 Mock 对象是否完整实现协议
- (void)testDataSourceProtocolConformance {
// 创建 Mock 对象
id mockDataSource = [OCMockObject mockForProtocol:@protocol(UITableViewDataSource)];
// 验证协议实现
[ProtocolConformanceChecker assertObjC:mockDataSource
conformsToProtocol:@protocol(UITableViewDataSource)
checkOptionalMethods:NO // UITableViewDataSource 只有必需方法
checkClassMethods:NO]; // 数据源协议通常只有实例方法
// 执行测试逻辑
// ...
}
场景二:框架初始化验证
objectivec
// 确保框架提供的基类正确实现协议
@implementation MyNetworkManager
+ (void)initialize {
if (self == [MyNetworkManager class]) {
// 验证类是否实现必要的协议
[ProtocolConformanceChecker assertObjC:self
conformsToProtocol:@protocol(MyNetworkProtocol)
checkOptionalMethods:YES // 检查所有可选方法
checkClassMethods:YES]; // 检查类方法
}
}
@end
场景三:关键路径的防御性检查
objectivec
// 在设置代理时进行验证
- (void)setDelegate:(id<MyCustomDelegate>)delegate {
// 只在调试模式下进行完整验证
#ifdef DEBUG
if (delegate) {
[ProtocolConformanceChecker assertObjC:delegate
conformsToProtocol:@protocol(MyCustomDelegate)
checkOptionalMethods:YES // 检查可选方法
checkClassMethods:NO]; // 代理协议通常只有实例方法
}
#endif
_delegate = delegate;
}
最佳实践
1. 调试与测试阶段
objectivec
// 在单元测试中全面验证
- (void)testProtocolImplementation {
[ProtocolConformanceChecker assertObjC:testObject
conformsToProtocol:@protocol(RequiredProtocol)
checkOptionalMethods:YES
checkClassMethods:YES];
}
2. 生产环境使用
less
// 使用条件编译控制检查行为
- (void)setupComponent:(id)component {
#ifdef DEBUG
// 调试模式下进行全面检查
[ProtocolConformanceChecker assertObjC:component conformsToProtocol:@protocol(ComponentProtocol) checkOptionalMethods:YES checkClassMethods:NO];
#else
// 生产环境下可选择性检查或记录日志
if ([component conformsToProtocol:@protocol(ComponentProtocol)]) {
// 基础检查通过
} else {
NSLog(@"Warning: Component does not conform to protocol");
}
#endif
}
扩展可能性
1. 批量验证支持
objectivec
// 扩展:支持批量验证多个协议
+ (void)assertObjC:(id)objc
conformsToProtocols:(NSArray<Protocol *> *)protocols
checkOptionalMethods:(BOOL)checkOptionalMethods
checkClassMethods:(BOOL)checkClassMethods;
2. 自定义验证回调
objectivec
// 扩展:支持自定义验证结果处理
typedef void(^ValidationCompletion)(BOOL success, NSArray<NSString *> *errors);
+ (void)validateObjC:(id)objc
conformsToProtocol:(Protocol *)protocol
checkOptionalMethods:(BOOL)checkOptionalMethods
checkClassMethods:(BOOL)checkClassMethods
completion:(ValidationCompletion)completion;
3. Swift 兼容性扩展
less
// 扩展:更好的 Swift 兼容性
NS_SWIFT_NAME(ProtocolConformanceChecker.validate(_:conformsTo:checkOptional:checkClass:))
+ (void)swift_validateObject:(id)objc
conformsToProtocol:(Protocol *)protocol
checkOptionalMethods:(BOOL)checkOptionalMethods
checkClassMethods:(BOOL)checkClassMethods;
总结
本文介绍了一个简洁高效的 Objective-C 协议一致性检查工具。通过深入理解 Objective-C 运行时机制,我们实现了一个能够全面验证协议实现的解决方案。
核心优势:
- ✅ 完整性:支持完整的协议继承链检查
- ✅ 灵活性:可配置的检查选项满足不同场景需求
- ✅ 准确性:严格的方法签名验证确保实现正确性
- ✅ 简洁性:去除了复杂的缓存逻辑,代码更易于理解和维护
- ✅ 实用性:清晰的错误报告帮助快速定位问题
适用场景:
- 单元测试和集成测试
- 框架和库的初始化验证
- 关键路径的防御性编程
- 协议实现的调试和验证
通过合理运用这个工具,可以在早期发现协议实现的问题,提高代码质量,减少运行时错误,构建更加健壮的 Objective-C 应用程序。