[swift] “내재적으로 래핑되지 않은 선택 사항”을 만드는 이유는 값이 있다는 것을 의미하기 때문입니다.

왜 일반 변수 나 상수를 만드는 것보다 “암시 적으로 래핑되지 않은 옵션”을 만들겠습니까? 그것이 성공적으로 풀릴 수 있다는 것을 안다면 왜 처음에 옵션을 작성합니까? 예를 들어, 왜 이럴까요 :

let someString: String! = "this is the string"

다음보다 더 유용 할 것입니다.

let someString: String = "this is the string"

“선택 사항이 상수 또는 변수에 ‘값이 없음’이 허용됨을 표시하지만”때때로 프로그램 구조에서 선택 사항이 항상 해당 값을 먼저 설정 한 후에 값이 있음을 알 수있는 경우 “는 무엇입니까? 처음에는 선택 사항입니까? 옵션이 항상 값을 가질 것이라는 것을 알고 있다면 선택이 아닌가?



답변

객체가 구성 및 구성되는 동안 nil 속성을 가질 수 있지만 나중에 변경 불가능하고 nil이 아닌 객체의 경우를 고려하십시오 (NSImage는 종종 이런 식으로 처리되지만 경우에 따라 변경하는 것이 유용합니다). 암시 적으로 래핑되지 않은 옵션은 상대적으로 낮은 안전 손실로 코드를 정리할 것입니다 (보증이 보장되는 한 안전 할 것입니다).

(편집) 분명히하기 위해 : 일반 옵션은 거의 항상 선호됩니다.


답변

Implicitly Unwrapped Optionals의 사용 사례를 설명하기 전에 Swift에있는 Optionals 및 Implicitly Unwrapped Optionals가 무엇인지 이해해야합니다. 그렇지 않은 경우 먼저 옵션에 대한 내 기사를 읽는 것이 좋습니다.

암시 적으로 래핑되지 않은 옵션을 사용하는 경우

하나는 암시 적으로 래핑되지 않은 옵션을 만드는 데는 두 가지 주요 이유가 있습니다. nilSwift 컴파일러는 항상 선택적으로 옵션을 풀어야하기 때문에 액세스 할 수없는 변수를 정의하는 것과 관련이 있습니다 .

1. 초기화 중에 정의 할 수없는 상수

초기화가 완료 될 때까지 모든 멤버 상수에는 값이 있어야합니다. 때로는 초기화하는 동안 상수를 올바른 값으로 초기화 할 수 없지만 액세스하기 전에 값을 가질 수는 있습니다.

Optional 변수를 사용하면 Optional이 자동으로 초기화되고 nil결국에는 포함 할 값이 변경되지 않기 때문에이 문제를 해결할 수 있습니다. 그러나 확실히 아는 변수가 끊임없이 풀리는 것은 고통이 될 수 있습니다. 암시 적으로 래핑되지 않은 선택 사항은 선택 사항과 동일한 이점을 제공하며 추가 혜택을 통해 어디에서나 명시 적으로 래핑 할 필요가 없습니다.

이에 대한 좋은 예는 뷰가로드 될 때까지 멤버 변수를 UIView 서브 클래스에서 초기화 할 수없는 경우입니다.

class MyView: UIView {
    @IBOutlet var button: UIButton!
    var buttonOriginalWidth: CGFloat!

    override func awakeFromNib() {
        self.buttonOriginalWidth = self.button.frame.size.width
    }
}

여기서는 뷰가로드 될 때까지 버튼의 원래 너비를 계산할 수 없지만 뷰의 awakeFromNib다른 메소드 (초기화 제외) 전에 호출 될 것임을 알고 있습니다. 클래스 전체에서 값이 명시 적으로 래핑되지 않도록 강제하는 대신 암시 적으로 래핑되지 않은 옵션으로 선언 할 수 있습니다.

2. 앱이 변수로부터 복구 할 수없는 경우 nil

이는 극히 드물지만 변수에 nil액세스 할 때 앱을 계속 실행할 수 없으면 테스트를 방해하는 데 시간이 낭비됩니다 nil. 일반적으로 앱이 계속 실행 되려면 반드시 참이어야하는 조건이있는 경우을 사용합니다 assert. 암시 적으로 래핑되지 않은 옵션에는 nil이 내장되어 있습니다. 그럼에도 불구하고, 선택 사항을 풀고 더 설명이없는 주장을 사용하는 것이 종종 좋습니다.

암시 적으로 래핑되지 않은 옵션을 사용하지 않는 경우

1. 지연 계산 된 멤버 변수

때로는 nil이 아니어야하는 멤버 변수가 있지만 초기화 중에 올바른 값으로 설정할 수없는 경우가 있습니다. 한 가지 해결책은 암시 적으로 래핑되지 않은 옵션을 사용하는 것이지만 더 좋은 방법은 지연 변수를 사용하는 것입니다.

class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // load contents and append to loadedContents
        return loadedContents
    }()
}

이제 멤버 변수 contents는 처음 액세스 될 때까지 초기화되지 않습니다. 이를 통해 클래스는 초기 값을 계산하기 전에 올바른 상태로 들어갈 수 있습니다.

참고 : 이것은 위에서 # 1과 모순되는 것처럼 보일 수 있습니다. 그러나 중요한 차이점이 있습니다. buttonOriginalWidth위는 속성에 액세스하기 전에 버튼 폭 변화하는 사람을 방지 할 수있는 viewDidLoad시 설정해야합니다.

2. 그 밖의 모든 곳

대부분의 경우, 잘못 사용하는 경우에 액세스 할 때 전체 앱이 중단되므로 암시 적으로 래핑되지 않은 선택 사항을 피해야합니다 nil. 변수가 nil 일 수 있는지 확실하지 않은 경우 항상 기본 옵션을 사용하는 것이 기본값입니다. 절대로 절대 변수가 nil되지 않는 것은 크게 아프지 않습니다.


답변

암시 적으로 래핑되지 않은 선택 사항은 실제로 커버 아래에서 선택 사항이 필요할 때 속성을 선택 사항으로 표시하는 데 유용합니다. 이것은 종종 서로에 대한 참조가 필요한 두 개의 관련 객체 사이에 “매듭을 묶는”데 필요합니다. 참조가 실제로 선택 사항 이 아닌 경우에는 의미가 있지만 쌍이 초기화되는 동안 이들 중 하나가 nil이어야합니다.

예를 들면 다음과 같습니다.

// These classes are buddies that never go anywhere without each other
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name)'s buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Big A")
println(a.myBuddyB.name)   // prints "Big A's buddy B"

모든 B인스턴스는 항상 유효한 myBuddyA참조를 가져야 하므로 사용자 가 인스턴스 를 선택 사항으로 취급하고 싶지는 않지만 참조 B하기 전에를 구성 할 수 있도록 선택적이어야 A합니다.

하나! 이러한 종류의 상호 참조 요구 사항은 종종 긴밀한 결합과 열악한 설계를 나타냅니다. 암시 적으로 래핑되지 않은 옵션에 의존하는 경우 상호 의존성을 제거하기 위해 리팩토링을 고려해야합니다.


답변

암시 적으로 래핑되지 않은 옵션은 기존 Cocoa 프레임 워크 및 해당 규칙과 상호 운용해야하는 하이브리드 환경에서보다 쾌적한 작업을 수행하는 동시에 실질적인 타협으로 Swift 컴파일러에 의해 시행되는 널 포인터없이보다 안전한 프로그래밍 패러다임으로 단계적으로 마이그레이션 할 수 있습니다.

스위프트 북 의 기본 장에서 암시 적으로 래핑되지 않은 옵션 섹션 은 다음과 같이 말합니다.

암시 적으로 래핑되지 않은 옵션은 옵션이 처음 정의 된 직후에 옵션의 값이 존재하는 것으로 확인 된 후 그 이후의 모든 시점에 존재한다고 가정 할 수있는 경우에 유용합니다. Swift에서 암시 적으로 래핑되지 않은 선택적 옵션의 기본 사용은 클래스 초기화 중입니다 ( 소유되지 않은 참조 및 암시 적으로 래핑되지 않은 선택적 속성)에 설명되어 있습니다.


암시 적으로 래핑되지 않은 옵션은 옵션을 사용할 때마다 자동으로 래핑 해제 할 수있는 권한을 부여하는 것으로 생각할 수 있습니다. 옵션을 사용할 때마다 옵션 이름 뒤에 느낌표를 표시하지 않고 선언 할 때 옵션 유형 뒤에 느낌표를 표시합니다.

이것은 nil 사용 속성이 사용 규칙을 통해 설정되고 클래스 초기화 중에 컴파일러로 시행 할 수없는 사용 사례로 귀결 됩니다. 예를 들어, UIViewControllerNIB 또는 스토리 보드에서 초기화되는 속성 (초기화는 별도의 단계로 나뉘지만 그 이후에는 viewDidLoad()일반적으로 속성이 존재한다고 가정 할 수 있음). 그렇지 않으면 컴파일러를 만족시키기 위해
코드의 주요 목적을 모호하게하기 위해 강제 언 래핑 ,
선택적 바인딩
또는 선택적 체인 을 사용해야했습니다
.

Swift 책의 위 부분은 Automatic Reference Counting 장도 참조하십시오 .

그러나 두 속성 모두 항상 값을 가져야하며 nil초기화가 완료된 후에는 두 속성이 없어야하는 세 번째 시나리오가 있습니다 . 이 시나리오에서는 한 클래스의 소유되지 않은 속성을 다른 클래스의 암시 적으로 래핑되지 않은 선택적 속성과 결합하는 것이 좋습니다.

이를 통해 초기화가 완료되면 참조주기를 피하면서 두 속성 모두에 직접 액세스 할 수 있습니다 (선택적 래핑 해제없이).

이것은 가비지 수집 언어가 아닌 단점으로 귀결됩니다. 따라서 유지주기의 중단은 프로그래머로서 당신에게 있으며 암묵적으로 래핑되지 않은 옵션은이 단점을 숨기는 도구입니다.

“코드에서 암시 적으로 래핑되지 않은 옵션을 언제 사용해야합니까?” 질문. 응용 프로그램 개발자는 대부분 Objective-C로 작성된 라이브러리의 메서드 서명에서 옵션 유형을 표현할 수있는 기능이 없습니다.

에서 코코아와 오브젝티브 C, 섹션 스위프트를 사용 전무 작업 :

Objective-C는 객체가 0이 아닌 것을 보증하지 않으므로 Swift는 가져온 Objective-C API에서 인수 유형 및 반환 유형의 모든 클래스를 선택적으로 만듭니다. Objective-C 오브젝트를 사용하기 전에 해당 오브젝트가 누락되지 않았는지 확인해야합니다.

경우에 따라서는 수도 절대적으로 오브젝티브 C의 메서드 나 속성은 결코 반환 없도록 nil객체 참조. 이 특별한 시나리오에서 객체를보다 편리하게 사용할 수 있도록 Swift는 객체 유형을 암시 적으로 래핑되지 않은 옵션 으로 가져옵니다 . 암시 적으로 랩핑되지 않은 선택적 유형에는 선택적 유형의 모든 안전 기능이 포함됩니다. 또한 확인하지 않고 직접 값에 액세스 할 수 있습니다nil또는 직접 포장을 푸십시오. 안전하게 래핑 해제하지 않고 이러한 종류의 옵션 유형의 값에 액세스하면 암시 적으로 래핑되지 않은 옵션이 값이 없는지 확인합니다. 값이 없으면 런타임 오류가 발생합니다. 결과적으로 값을 찾을 수없는 경우가 아니면 암시 적으로 래핑되지 않은 옵션을 직접 확인하고 래핑 해제해야합니다.

… 그리고 저쪽에 누워 용


답변

한 줄 (또는 여러 줄) 간단한 예제는 옵션의 동작을 잘 다루지 않습니다. 예, 변수를 선언하고 바로 값을 제공하면 옵션에 아무런 의미가 없습니다.

지금까지 내가 본 가장 좋은 사례는 객체 초기화 후에 발생하는 설정이며 그 다음에 “보증 된”사용을 통해 해당 설정을 따릅니다 (예 : 뷰 컨트롤러).

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}

printSize뷰가로드 된 후 호출 될 것임을 알고 있습니다. 뷰 내부의 컨트롤에 연결된 액션 메서드이므로 다른 방법으로 호출하지 않아야합니다. 그래서 우리는 선택적으로 확인 / 바인딩을 할 수 !있습니다. Swift는 (적어도 Apple이 정지 문제를 해결할 때까지) 그 보증을 인식 할 수 없으므로 컴파일러에 존재한다고 알려주십시오.

그러나 이것은 타입 안전성을 어느 정도 떨어 뜨립니다. 암시 적으로 래핑되지 않은 옵션이있는 곳은 “보증인”이 항상 유지되지 않는 경우 앱이 중단 될 수있는 곳이므로 드물게 사용하는 기능입니다. 게다가, 사용! 항상 사용하면 소리를 지르는 것처럼 들리며 아무도 그것을 좋아하지 않습니다.


답변

Apple은 Swift Programming Language- > 에서 훌륭한 예를 보여줍니다. Automatic Reference Counting- > 클래스 인스턴스 간의 강력한 참조 사이클 해결 -> 소유되지 않은 참조 및 암시 적으로 래핑되지 않은 선택적 속성

class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

의 초기화 프로그램 City은의 초기화 프로그램 내에서 호출됩니다 Country. 그러나 2 단계 초기화에 설명 된대로 새 인스턴스가 완전히 초기화 될 때까지 의 초기화 프로그램 이 초기화 프로그램에 Country전달 될 수 없습니다 .selfCityCountry

이 요구 사항에 대처하기 위해 capitalCity속성을 Country암시 적으로 래핑되지 않은 선택적 속성으로 선언합니다 .


답변

강제적 래핑 해제에 대한 이론적 근거를 먼저 살펴보면 암묵적 옵션의 이론적 근거를 쉽게 설명 할 수 있습니다.

!를 사용하여 선택적 (암시 적 또는 비 적합) 강제 래핑 해제 operator는 코드에 버그가 없으며 옵션에 이미 래핑되지 않은 값이 있다는 것을 의미합니다. 없이! 연산자, 당신은 아마도 선택적 바인딩으로 주장 할 것입니다 :

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }

그다지 좋지 않은

println("\(value!)")

이제 암시 적 옵션을 사용하면 가능한 모든 흐름에서 항상 줄 바꿈 할 때 항상 값을 가질 것으로 예상되는 옵션을 갖는 것을 표현할 수 있습니다 . 따라서 쓰기 요구 사항을 완화하여 더 많은 도움을 줄 수 있습니다. 매번 포장을 풀고 흐름에 대한 가정이 잘못 된 경우 런타임이 여전히 오류가 발생하는지 확인하십시오.