PAYATHON 2026

调用 ZooZ payment Interface 时出现 NSUnknownKeyException

支付小周

结论

这个异常通常与付款金额、币种或 buyMore 方法无关。支付界面加载 XIB/Storyboard 时,其中保存了一个名为 pageControl 的 IBOutlet/KVC 连接,但实际创建的控制器对象没有对应的属性或实例变量。UIKit 调用 setValue:forKey: 后,就会抛出 NSUnknownKeyException。

常见原因包括:

  • ZooZ SDK 的二进制文件和资源文件版本不一致;
  • 项目中残留了旧版 ZooZ XIB、Storyboard 或 Bundle;
  • 代码中的 pageControl IBOutlet 已被删除或改名,界面连接却没有同步清除;
  • 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:

  1. 选中相应的 View Controller;
  2. 打开 Connections Inspector;
  3. 找到 pageControl;
  4. 删除带有警告标记或指向不存在属性的连接;
  5. 如果仍需使用该控件,将它重新连接到正确的 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 维护方,以核实兼容性问题。

备注:内容仅供参考。