Understanding fixed error: Sending NSError *const __strong* to parameter
Understanding fixed error: Sending NSError *const __strong* to parameter
在写我原来的问题时,我无意中解决了我的问题。我想了解为什么我的更改会导致修复。
我写了一个 class 方法,它接受一个 NSString
和一个 NSError **
。如果字符串在我的应用程序中是有效密码,方法 returns YES
,如果不是,则方法 NO
。如果返回 NO
,则在方法内部设置通过引用传递的 NSError
。
验证方法如下:
@interface PasswordValidator
+ (BOOL)isValid:(NSString *)password error:(NSError **)error {
NSError *err = nil;
if (!password || password.length < 1) {
err = [NSError errorWithDomain:...];
}
// Rest of logic omitted for brevity
if (err) {
if (error) {
*error = err;
}
return NO;
}
return YES;
}
@end
在我的测试中,我错误地尝试检查 NSError
对象是否存在,如下所示:
it(@"should set error if password is nil", ^{
NSError *error;
expect([PasswordValidator isValid:nil error:&error]).toNot.beNil();
});
忽略测试包含逻辑错误的事实,代码导致编译器抱怨错误:Sending NSError *const __strong* to parameter of type 'NSError *__autoreleasing* changes retain/release properters of pointer
。
更新测试后,PasswordValidator:isValid:error
的调用发生在单独的行上,错误消失了:
// The test below did not cause compilation issues.
it(@"should return NO if password is nil", ^{
NSError *error;
[HAYPasswordValidator isValid:nil error:&error];
expect(error).toNot.beNil();
});
我的问题是,为什么编译器最初会抱怨?将方法调用分离到单独的行来解决问题的原因是什么?
一个 NSError** 参数实际上是一个 NSError __autoreleasing 参数。如果传递 &error,编译器将创建一个本地 NSError* __autoreleasing 变量,将其传递给方法,并将结果存储到错误中。这有点复杂,但大多数人从来没有注意到。
我怀疑 expect 宏或函数做了一些复杂的事情,使编译器变得复杂,并且它会抱怨。您可以检查预处理器输出以查看实际编译的内容。我不确定错误消息中的 "const" 是从哪里来的。
您使用的是什么测试工具?如果那个 expect()
函数实际上是一个将表达式包装在一个块中的宏,那么这可能会导致编译器以这种方式抱怨。
但是,你的固定测试是错误的.
您无法直接测试 nil
/non-nil
的错误。如果方法 returns NO
(或 nil
,通常是任何指示错误的方法),error
将仅设置为定义的值。然后并且只有那时才会定义 error
。
考虑 isValid:error:
方法的实施。如果没有错误,则不会触及 error
参数。如果 error
在进入该方法时未初始化,它在成功案例中仍将未初始化,如果您只测试 error
本身,您的测试用例可能会在不应该失败的情况下失败。
如果您尝试在块中捕获 NSError ** 变量,您也可能会遇到此错误。捕获 __autoreleasing 个变量是不可能的朋友。所需要做的就是在块内重新声明 NSError **。
在写我原来的问题时,我无意中解决了我的问题。我想了解为什么我的更改会导致修复。
我写了一个 class 方法,它接受一个 NSString
和一个 NSError **
。如果字符串在我的应用程序中是有效密码,方法 returns YES
,如果不是,则方法 NO
。如果返回 NO
,则在方法内部设置通过引用传递的 NSError
。
验证方法如下:
@interface PasswordValidator
+ (BOOL)isValid:(NSString *)password error:(NSError **)error {
NSError *err = nil;
if (!password || password.length < 1) {
err = [NSError errorWithDomain:...];
}
// Rest of logic omitted for brevity
if (err) {
if (error) {
*error = err;
}
return NO;
}
return YES;
}
@end
在我的测试中,我错误地尝试检查 NSError
对象是否存在,如下所示:
it(@"should set error if password is nil", ^{
NSError *error;
expect([PasswordValidator isValid:nil error:&error]).toNot.beNil();
});
忽略测试包含逻辑错误的事实,代码导致编译器抱怨错误:Sending NSError *const __strong* to parameter of type 'NSError *__autoreleasing* changes retain/release properters of pointer
。
更新测试后,PasswordValidator:isValid:error
的调用发生在单独的行上,错误消失了:
// The test below did not cause compilation issues.
it(@"should return NO if password is nil", ^{
NSError *error;
[HAYPasswordValidator isValid:nil error:&error];
expect(error).toNot.beNil();
});
我的问题是,为什么编译器最初会抱怨?将方法调用分离到单独的行来解决问题的原因是什么?
一个 NSError** 参数实际上是一个 NSError __autoreleasing 参数。如果传递 &error,编译器将创建一个本地 NSError* __autoreleasing 变量,将其传递给方法,并将结果存储到错误中。这有点复杂,但大多数人从来没有注意到。
我怀疑 expect 宏或函数做了一些复杂的事情,使编译器变得复杂,并且它会抱怨。您可以检查预处理器输出以查看实际编译的内容。我不确定错误消息中的 "const" 是从哪里来的。
您使用的是什么测试工具?如果那个 expect()
函数实际上是一个将表达式包装在一个块中的宏,那么这可能会导致编译器以这种方式抱怨。
但是,你的固定测试是错误的.
您无法直接测试 nil
/non-nil
的错误。如果方法 returns NO
(或 nil
,通常是任何指示错误的方法),error
将仅设置为定义的值。然后并且只有那时才会定义 error
。
考虑 isValid:error:
方法的实施。如果没有错误,则不会触及 error
参数。如果 error
在进入该方法时未初始化,它在成功案例中仍将未初始化,如果您只测试 error
本身,您的测试用例可能会在不应该失败的情况下失败。
如果您尝试在块中捕获 NSError ** 变量,您也可能会遇到此错误。捕获 __autoreleasing 个变量是不可能的朋友。所需要做的就是在块内重新声明 NSError **。