【问题标题】:Can't unwrap Optional.None Error, Yet Not Using an Optional无法打开 Optional.None 错误,但未使用 Optional
【发布时间】:2014-07-07 16:03:18
【问题描述】:

我正在尝试用 Swift 编写一个 HMAC 类,首先用 Objective C 编写它,然后使用 Bridging-Header 使该类可用于我的 Swift 代码。如果我粘贴了太多代码,我很抱歉,但我想了解为什么会发生这种情况,以便更好地理解 Swift 和 Objective C 的接口,以及如何解决我遇到的错误。 “TBGHMAC.calculateWithAlgorithm: forKey: and Data:”函数会产生“致命错误:无法解开 Optional.None”错误。

从顶部开始,这里是我的 Swift 代码中调用 Swift 代码的 sn-p

    let key : String = "" // start with empty string"
    let data : String = "" // start with empty string"

    let signature : String = TBGHMAC.calculateWithAlgorithm(HMACAlgorithm.SHA256, forKey: key, andData: data)

    println(signature)

这里是 TBGHMAC 头文件

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>

@interface TBGHMAC : NSObject


typedef NS_ENUM(NSInteger, HMACAlgorithm)
{
SHA1,
MD5,
SHA256,
SHA384,
SHA512,
SHA224
};

+ (NSString *)calculateWithAlgorithm:(HMACAlgorithm)algorithm forKey:(NSString*)key andData:(NSString *)data;

+ (NSInteger)digestLengthForAlgorithm:(HMACAlgorithm)algorithm;

@end

最后是实现文件

+ (NSString *)calculateWithAlgorithm:(HMACAlgorithm)algorithm forKey:(NSString *)key andData:(NSString *)data
{

    NSUInteger keyNumberOfBytes = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    void *keyBuffer = malloc(keyNumberOfBytes);
    NSUInteger keyUsedLength = 0;
    NSRange keyRange = NSMakeRange(0, [key length]);
    BOOL keyResult = [key getBytes:keyBuffer maxLength:keyNumberOfBytes usedLength:&keyUsedLength encoding:NSUTF8StringEncoding options:0 range:keyRange remainingRange:NULL];

    NSUInteger dataNumberOfBytes = [data lengthOfBytesUsingEncoding:NSUnicodeStringEncoding];
    void *dataBuffer = malloc(dataNumberOfBytes);
    NSUInteger dataUsedLength = 0;
    NSRange dataRange = NSMakeRange(0, [data length]);
    BOOL dataResult = [data getBytes:dataBuffer maxLength:dataNumberOfBytes usedLength:&dataUsedLength encoding:NSUTF8StringEncoding options:0 range:dataRange remainingRange:NULL];

    NSInteger digestLength = [self digestLengthForAlgorithm:algorithm];
    unsigned char hmac[digestLength];

    CCHmac(algorithm, &keyBuffer, strlen(keyBuffer), &dataBuffer, strlen(dataBuffer), &hmac);

    NSData *hmacBytes = [NSData dataWithBytes:hmac length:sizeof(hmac)];

    NSString* returnStr = [[NSString alloc] initWithData:hmacBytes encoding:NSUTF8StringEncoding];

    free(keyBuffer);
    free(dataBuffer);

    return returnStr;
}

+ (NSInteger)digestLengthForAlgorithm:(HMACAlgorithm)algorithm
{
    switch (algorithm)
    {
        case MD5: return CC_MD5_DIGEST_LENGTH;
        case SHA1: return CC_SHA1_DIGEST_LENGTH;
        case SHA224: return CC_SHA224_DIGEST_LENGTH;
        case SHA256: return CC_SHA256_DIGEST_LENGTH;
        case SHA384: return CC_SHA384_DIGEST_LENGTH;
        case SHA512: return CC_SHA512_DIGEST_LENGTH;
        default: return 0;
    }
}

@end

请指出为什么会出现此错误,以及如何解决。我不明白这个错误是如何产生的,因为我没有使用任何 Optionals,Xcode 也没有试图纠正我使用 Optionals。

【问题讨论】:

标签: swift hmac


【解决方案1】:

Objective-C 方法

+ (NSString *)calculateWithAlgorithm:(HMACAlgorithm)algorithm forKey:(NSString*)key andData:(NSString *)data;

暴露给 Swift

class func calculateWithAlgorithm(algorithm: HMACAlgorithm, forKey key: String!, andData data: String!) -> String!

它返回一个(隐式展开)可选字符串,因为Objective-C方法返回的NSString可能是nil。

将返回值转换为String in

let signature : String = BGHMAC.calculateWithAlgorithm(...)

如果返回值为nil(“无值”),则会导致运行时异常。因此,您应该测试返回值 使用前:

let signature : String! = TBGHMAC.calculateWithAlgorithm(HMACAlgorithm.SHA256, forKey: key, andData: data)
if signature {
    println(signature)
} else {
    println("failed")
}

但是为什么calculateWithAlgorithm() 返回nil?问题出在这里:

NSString* returnStr = [[NSString alloc] initWithData:hmacBytes encoding:NSUTF8StringEncoding];

消息摘要是一个非常任意的字节序列,无法解释 作为 UTF-8 字符串,因此 returnStr 为零。

为了解决您的问题,您可以将消息摘要转换为 使用 Base64 编码的字符串:

NSString* returnStr = [hmacBytes base64EncodedStringWithOptions:0];

【讨论】:

  • 谢谢@Martin R,尽管我有几个后续问题。 1.当编译器将Obj-C转换成Swift,并把参数变成String类型!做“字符串!”表示传入的字符串是常规可选的,还是隐式展开的可选? 2、为什么选择base64encoding,UTF-8不使用定宽字符值吗?从 NSData 类型输出一串十六进制数字的习惯方法是什么?
  • 1) 你是对的,返回值是一个隐式展开的可选值,我已经相应地更新了答案。 - 2) UTF-8 使用 1、2、3 或 4 个字节来编码一个 Unicode 字符。 “任意”字节序列通常不是有效的 UTF-8 序列。 Base64 是一种惯用的方式,另一种是将每个字节转换为十六进制。 (Base64 更短。)
  • 这实际上是行不通的,因为 base64EncodedStringWithOptions: 返回 (NSData *),所以我并没有真正接近从 NSData 转换为 NSString。
  • @almel:这很奇怪。在developer.apple.com/library/ios/documentation/cocoa/reference/…:该方法记录为- (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)options,因此它返回一个NSString。实际上我在发布答案之前测试了代码。也许你把它和base64EncodedDataWithOptions 混淆了?
  • 哦不,我现在看到我使用的是 base64EncodedDataWithOptions:,而不是 String。对于那个很抱歉。您是否知道为什么我的 HMAC 方法没有通过 HMAC RFC 和其他测试站点上列出的任何测试向量?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
相关资源
最近更新 更多