困惑:非nil值解包了nil,if-let子句对nil值执行了块

时间:2018-08-04 18:34:52

标签: ios swift

我正在将类型为Data的非nil值分配给非可选属性,然后将其分配给可选属性,该属性最终使用该数据实例化图像。当可选参数通过if-let子句时,它的块执行,并抛出错误:

  

致命错误:解开可选值时意外发现nil

绝对不要 stillImageData 拆零。

import UIKit
import AVFoundation
import Photos

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {

var photoOutput: AVCapturePhotoOutput!
var stillImageData: Data!

@IBAction func handleTouch(_ sender: Any) {
    let photoSettings = AVCapturePhotoSettings(format: nil)
    photoSettings.isAutoStillImageStabilizationEnabled = true
    if photoOutput.supportedFlashModes.contains(AVCaptureDevice.FlashMode.auto) {
        photoSettings.flashMode = .auto
    }
    photoOutput.capturePhoto(with: photoSettings, delegate: self)
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized: // The user has previously granted access to the camera.
        self.setupCaptureSession()

    case .notDetermined: // The user has not yet been asked for camera access.
        AVCaptureDevice.requestAccess(for: .video) { granted in
            if granted {
                self.setupCaptureSession()
            }
        }

    case .denied: // The user has previously denied access.
        return
    case .restricted: // The user can't grant access due to restrictions.
        return
    }

    PHPhotoLibrary.requestAuthorization { status in
        guard status == .authorized else { return }

        // Use PHPhotoLibrary.shared().performChanges(...) to add assets.
    }
}

func setupCaptureSession() {
    let captureSession = AVCaptureSession()
    // Connect inputs and outputs to the session
    captureSession.beginConfiguration()
    let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
                                              for: .video, position: .unspecified)
    guard
        let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!),
        captureSession.canAddInput(videoDeviceInput)
        else { return }
    captureSession.addInput(videoDeviceInput)
    photoOutput = AVCapturePhotoOutput()
    guard captureSession.canAddOutput(photoOutput) else { return }
    captureSession.sessionPreset = .photo
    captureSession.addOutput(photoOutput)
    captureSession.commitConfiguration()
    // Display a camera preview
    let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.frame = view.bounds
    previewLayer.videoGravity = .resizeAspectFill
    view.layer.addSublayer(previewLayer)
    // Run the capture session
    captureSession.startRunning()
}

// MARK: - Photo capture

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    // guard error != nil else { print("Error capturing photo: \(error!)"); return }

    self.stillImageData = photo.fileDataRepresentation()

    performSegue(withIdentifier: "showDetail", sender: self)
}

// MARK: - Navigation

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail" {
        let controller = (segue.destination as! UINavigationController).topViewController as! RootViewController
        controller.detailItem = stillImageData
    }
}

}


import UIKit
import Photos

class RootViewController: UIViewController {

@IBOutlet weak var imageView: UIImageView!

func configureView() {
    if let stillImageData = detailItem {
        imageView.image = UIImage(data: stillImageData)
    }
}

var detailItem: Data? {
    didSet {
        configureView()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
}

@IBAction func dismissViewController(_ sender: Any) {
    dismiss(animated: true, completion: nil)
}

@IBAction func savePhotoToPhotosLibrary(_ sender: Any) {
    PHPhotoLibrary.requestAuthorization { status in
        guard status == .authorized else { return }

        PHPhotoLibrary.shared().performChanges({
            // Add the captured photo's file data as the main resource for the Photos asset.
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, data: self.detailItem!, options: nil)
        }, completionHandler: { success, error in
            if !success { NSLog("error creating asset: \(error!)") }
        })
    }
}
/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destination.
    // Pass the selected object to the new view controller.
}
*/

}

如果有人可以伸出援手,将不胜感激。 1)为什么为 stillImageData detailItem 分配了非nil值,它们是否为nil,以及2)为什么if-let子句执行其块?

1 个答案:

答案 0 :(得分:1)

stillImageDatadetailItem不是nil,但是imageViewnil

在执行prepare(for时,在controller.detailItem = stillImageData中尚未加载目标控制器的视图,因此尚未连接所有插座。在configureView()中访问插座时,会发生崩溃。

在这种情况下,请在configureView()中呼叫viewDidLoad

var detailItem: Data? 

override func viewDidLoad() {
    super.viewDidLoad()
    configureView()
}

另一方面,如果detailItem要多次更新,请检查插座

var detailItem: Data? {
    didSet {
        if imageView != nil { configureView() }
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    configureView()
}