调用 ZooZ payment Interface 时出现 NSUnknownKeyException
结论
这个异常通常与付款金额、币种或 buyMore 方法无关。支付界面加载 XIB/Storyboard 时,其中保存了一个名为 pageControl 的 IBOutlet/KVC 连接,但实际创建的控制器对象没有对应的属性或实例变量。UIKit 调用 setValue:forKey: 后,就会抛出 NSUnknownKeyException。
常见原因包括:
- ZooZ SDK 的二进制文件和资源文件版本不一致;
- 项目中残留了旧版 ZooZ XIB、Storyboard 或 Bundle;
- 代码中的
pageControlIBOutlet 已被删除或改名,界面连接却没有同步清除; - XIB/Storyboard 的 Custom Class 配置不正确。
为什么会出现 setValue:forUndefinedKey:
UIKit 加载 XIB/Storyboard 时,会恢复其中保存的 IBOutlet 连接。这个过程相当于执行:
[viewController setValue:pageControlObject forKey:@"pageControl"];
如果 viewController 对应的类没有 pageControl 属性、实例变量或兼容的 KVC setter,系统最终会调用:
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
它的默认实现会直接抛出 NSUnknownKeyException。
异常中的重要线索是:
this class is not key value coding-compliant for the key pageControl
这说明界面资源与运行时类定义不匹配,而不是 ZooZPaymentRequest 缺少某个支付参数。
如何定位具体来源
1. 添加异常断点
在 Xcode 的 Breakpoint Navigator 中添加 Exception Breakpoint,选择 Objective-C Exceptions,然后再次点击 buyMore。
程序会在异常实际抛出的位置暂停,不会只显示后续的崩溃调用栈。此时需要检查:
- 当前加载的是哪个 XIB、Storyboard 或 Bundle;
- 系统正尝试为哪个类的对象设置
pageControl; - 调用是否发生在 ZooZ SDK 内部。
完整的异常信息通常包含具体类名,例如:
[SomeViewController setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key pageControl
这个类名是定位问题最有用的线索。
2. 检查 IBOutlet 连接
如果异常中的类属于自己的项目,请在 Interface Builder 中打开对应的 XIB 或 Storyboard:
- 选中相应的 View Controller;
- 打开 Connections Inspector;
- 找到
pageControl; - 删除带有警告标记或指向不存在属性的连接;
- 如果仍需使用该控件,将它重新连接到正确的 IBOutlet。
同时检查 Identity Inspector 中的 Custom Class,确认它与代码中的控制器类一致。
3. 检查 ZooZ SDK 资源是否匹配
如果异常中的类属于 ZooZ SDK,就需要检查 SDK 的集成文件。ZooZ 的界面资源可能位于单独的 .bundle、.xib 或其他资源包中。这些资源必须与当前链接的 ZooZ 库来自同一个 SDK 版本。
可以依次检查:
- 删除项目中的旧版 ZooZ 静态库、Framework 和资源 Bundle;
- 确认 Build Phases → Copy Bundle Resources 中没有重复或旧版 ZooZ 资源;
- 使用同一份 SDK 安装包重新添加二进制文件及配套资源;
- 清理 Build Folder,删除设备或模拟器上的旧应用,再重新安装;
- 必要时清理 Derived Data,以免继续使用旧的已编译 XIB。
无论是只替换 ZooZ 库而保留旧资源,还是只替换资源而保留旧库,都可能引发这个异常。
如果 pageControl 属于自己的控制器
如果界面确实需要这个控件,可以恢复对应的属性:
#import <UIKit/UIKit.h>
@interface PaymentViewController : UIViewController
@property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
@end
非 ARC 项目还需要正确释放它:
@implementation PaymentViewController
@synthesize pageControl;
- (void)dealloc
{
self.pageControl = nil;
[super dealloc];
}
@end
如果项目启用了 ARC,则不能调用 release、autorelease 或 [super dealloc],属性通常可以写成:
@property (nonatomic, weak) IBOutlet UIPageControl *pageControl;
不要为了避开第三方 SDK 的异常,在无关的控制器中随意添加同名属性。应先确认抛出异常的对象类型,以及 pageControl 连接来自哪个界面文件。
支付调用代码是否需要修改
给出的支付请求代码没有直接操作 pageControl。如果异常是在执行下面这行代码后出现的:
[zooz openPayment:req forAppKey:@"951bde04-5a26-4200-a94e-34d775a1e5f9"];
那么更可能是 openPayment:forAppKey: 加载 ZooZ 支付界面时触发了资源连接错误,而不是请求对象配置有误。
可以暂时简化请求来验证:
- (IBAction)buyMore
{
ZooZ *zooz = [ZooZ sharedInstance];
zooz.sandbox = YES;
ZooZPaymentRequest *request =
[zooz createPaymentRequestWithTotal:12.1
invoiceRefNumber:@"test invoice ref-1234"
delegate:self];
request.currencyCode = @"EUR";
[zooz openPayment:request
forAppKey:@"951bde04-5a26-4200-a94e-34d775a1e5f9"];
}
如果精简后仍然出现相同的 pageControl 异常,基本可以确定问题出在支付界面的加载阶段。
注意事项
setValue:forUndefinedKey:通常表示界面资源与类定义不一致,修改付款金额或地址字段无法解决这个问题。- 不要通过重写
setValue:forUndefinedKey:来静默忽略错误。这样只会掩盖已经失效的 IBOutlet,之后仍可能出现界面异常或空指针问题。 - 示例中的
autorelease仅适用于非 ARC 工程。ARC 工程应将其移除:
ZooZInvoiceItem *item =
[[ZooZInvoiceItem alloc] initWithPrice:12.1
quantity:1
name:@"T-Shirt"];
- 生产环境的 App Key 不宜散落在源码或公开日志中。仅凭当前异常信息,无法判断示例密钥是否仍然有效。
- 如果异常确定来自 ZooZ 自带资源,并且二进制文件与资源已经完全匹配,请将异常中的具体类名、SDK 版本、Xcode/iOS 环境和触发调用栈提交给 SDK 维护方,以核实兼容性问题。
备注:内容仅供参考。
