IT story

스위프트 : nil에 대한 옵션 테스트

hot-time 2020. 7. 13. 07:57
반응형

스위프트 : nil에 대한 옵션 테스트


Xcode 6 Beta 4를 사용하고 있습니다. 옵션을 적절히 테스트하는 방법을 알 수없는 이상한 상황이 있습니다.

선택적 xyz가있는 경우 올바른 테스트 방법입니다.

if (xyz) // Do something

또는

if (xyz != nil) // Do something

문서는 첫 번째 방법으로 수행한다고 말하지만 때로는 두 번째 방법이 필요하며 컴파일러 오류를 생성하지 않지만 두 번째 방법은 컴파일러 오류를 생성한다는 것을 알았습니다.

내 구체적인 예는 신속하게 연결된 GData XML 파서를 사용하는 것입니다.

let xml = GDataXMLDocument(
    XMLString: responseBody,
    options: 0,
    error: &xmlError);

if (xmlError != nil)

내가 방금 한 경우 여기 :

if xmlError

항상 true를 반환합니다. 그러나 내가 할 경우 :

if (xmlError != nil)

그런 다음 (Objective-C에서 작동하는 방식으로) 작동합니다.

GData XML과 누락 된 옵션을 처리하는 방식이 있습니까?


Xcode Beta 5에서는 더 이상 할 수 없습니다.

var xyz : NSString?

if xyz {
  // Do something using `xyz`.
}

오류가 발생합니다.

프로토콜 'BooleanType.Protocol'을 준수하지 않습니다

다음 형식 중 하나를 사용해야합니다.

if xyz != nil {
   // Do something using `xyz`.
}

if let xy = xyz {
   // Do something using `xy`.
}

if조건 내에서 다른 이름을 가진 변수에 할당하는 대신 다른 답변에 추가하려면 다음을 수행하십시오 .

var a: Int? = 5

if let b = a {
   // do something
}

다음과 같은 변수 이름을 재사용 할 수 있습니다.

var a: Int? = 5

if let a = a {
    // do something
}

이렇게하면 소재 변수 이름이 부족 해지는 것을 피할 수 있습니다 ...

이것은 Swift에서 지원되는 가변 그림자활용 합니다.


옵션을 사용하는 가장 직접적인 방법 중 하나는 다음과 같습니다.

예를 들어 xyz, 선택 유형 이라고 가정하십시오 Int?.

if let possXYZ = xyz {
    // do something with possXYZ (the unwrapped value of xyz)
} else {
    // do something now that we know xyz is .None
}

이 방법 xyz으로 값 포함되어 있는지 테스트 하고 있으면 해당 값으로 즉시 작업 할 수 있습니다.

컴파일러 오류와 관련하여 유형 UInt8은 선택 사항이 아니므로 ( 'no'는 아님) 주로 변환 할 수 없습니다 nil. 작업하는 변수가 선택 사항인지 확인한 후 변수로 취급하십시오.


Swift 3.0, 4.0

There are mainly two ways of checking optional for nil. Here are examples with comparison between them

1. if let

if let is the most basic way to check optional for nil. Other conditions can be appended to this nil check, separated by comma. The variable must not be nil to move for the next condition. If only nil check is required, remove extra conditions in the following code.

Other than that, if x is not nil, the if closure will be executed and x_val will be available inside. Otherwise the else closure is triggered.

if let x_val = x, x_val > 5 {
    //x_val available on this scope
} else {

}

2. guard let

guard let can do similar things. It's main purpose is to make it logically more reasonable. It's like saying Make sure the variable is not nil, otherwise stop the function. guard let can also do extra condition checking as if let.

The differences are that the unwrapped value will be available on same scope as guard let, as shown in the comment below. This also leads to the point that in else closure, the program has to exit the current scope, by return, break, etc.

guard let x_val = x, x_val > 5 else {
    return
}
//x_val available on this scope

From swift programming guide

If Statements and Forced Unwrapping

You can use an if statement to find out whether an optional contains a value. If an optional does have a value, it evaluates to true; if it has no value at all, it evaluates to false.

So the best way to do this is

// swift > 3
if xyz != nil {}

and if you are using the xyz in if statement.Than you can unwrap xyz in if statement in constant variable .So you do not need to unwrap every place in if statement where xyz is used.

if let yourConstant = xyz{
      //use youtConstant you do not need to unwrap `xyz`
}

This convention is suggested by apple and it will be followed by devlopers.


Although you must still either explicitly compare an optional with nil or use optional binding to additionally extract its value (i.e. optionals are not implicitly converted into Boolean values), it's worth noting that Swift 2 has added the guard statement to help avoid the pyramid of doom when working with multiple optional values.

In other words, your options now include explicitly checking for nil:

if xyz != nil {
    // Do something with xyz
}

Optional binding:

if let xyz = xyz {
    // Do something with xyz
    // (Note that we can reuse the same variable name)
}

And guard statements:

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    // e.g. by calling return, break, continue, or throw
    return
}

// Do something with xyz, which is now guaranteed to be non-nil

Note how ordinary optional binding can lead to greater indentation when there is more than one optional value:

if let abc = abc {
    if let xyz = xyz {
        // Do something with abc and xyz
    }        
}

You can avoid this nesting with guard statements:

guard let abc = abc else {
    // Handle failure and then exit this code block
    return
}

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    return
}

// Do something with abc and xyz

Instead of if, ternary operator might come handy when you want to get a value based on whether something is nil:

func f(x: String?) -> String {
    return x == nil ? "empty" : "non-empty"
}

Another approach besides using if or guard statements to do the optional binding is to extend Optional with:

extension Optional {

    func ifValue(_ valueHandler: (Wrapped) -> Void) {
        switch self {
        case .some(let wrapped): valueHandler(wrapped)
        default: break
        }
    }

}

ifValue receives a closure and calls it with the value as an argument when the optional is not nil. It is used this way:

var helloString: String? = "Hello, World!"

helloString.ifValue {
    print($0) // prints "Hello, World!"
}

helloString = nil

helloString.ifValue {
    print($0) // This code never runs
}

You should probably use an if or guard however as those are the most conventional (thus familiar) approaches used by Swift programmers.


One option that hasn't specifically been covered is using Swift's ignored value syntax:

if let _ = xyz {
    // something that should only happen if xyz is not nil
}

I like this since checking for nil feels out of place in a modern language like Swift. I think the reason it feels out of place is that nil is basically a sentinel value. We've done away with sentinels pretty much everywhere else in modern programming so nil feels like it should go too.


var xyz : NSDictionary?

// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary() 
// case 3: do nothing

if xyz { NSLog("xyz is not nil.") }
else   { NSLog("xyz is nil.")     }

This test worked as expected in all cases. BTW, you do not need the brackets ().


Now you can do in swift the following thing which allows you to regain a little bit of the objective-c if nil else

if textfieldDate.text?.isEmpty ?? true {

}

If you have conditional and would like to unwrap and compare, how about taking advantage of the short-circuit evaluation of compound boolean expression as in

if xyz != nil && xyz! == "some non-nil value" {

}

Granted, this is not as readable as some of the other suggested posts, but gets the job done and somewhat succinct than the other suggested solutions.


Also you can use Nil-Coalescing Operator

The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.

let value = nullableValue ?? defaultValue

If nullableValue is nil, it automatically assigns value to defaultValue


Swift 5 Protocol Extension

Here is an approach using protocol extension so that you can easily inline an optional nil check:

 public extension Optional {

    var isNil: Bool {

        guard case Optional.none = self else {
            return false
        }

        return true

    }

}

Usage

var myValue: String?

if !myValue.isNil {
    // do something
}

참고URL : https://stackoverflow.com/questions/25097727/swift-testing-optionals-for-nil

반응형