Ios 在Swift中,不是';实例化函数之外的对象不是最佳做法吗?

Ios 在Swift中,不是';实例化函数之外的对象不是最佳做法吗?,ios,swift,Ios,Swift,在Swift中,我们似乎推断出类内的类型,而不是函数外的类型。我理解,如果一个变量只在一个函数中声明,那么它将只在给定的范围内存在。实例化函数之外的对象不是最好的做法吗?这样我们可以在编写viewController时引用相同的对象,同时避免崩溃的可能性?如果不是,那么在ViewController顶部推断变量,然后在函数中实例化对象的目的是什么 下面是我在教程中遵循的示例代码。请注意,mapView是如何在viewController顶部推断出来的,但在loadView方法中实例化的。这是否会

在Swift中,我们似乎推断出类内的类型,而不是函数外的类型。我理解,如果一个变量只在一个函数中声明,那么它将只在给定的范围内存在。实例化函数之外的对象不是最好的做法吗?这样我们可以在编写viewController时引用相同的对象,同时避免崩溃的可能性?如果不是,那么在ViewController顶部推断变量,然后在函数中实例化对象的目的是什么

下面是我在教程中遵循的示例代码。请注意,mapView是如何在viewController顶部推断出来的,但在loadView方法中实例化的。这是否会使mapView对象只能由loadView函数访问,而不能由其他方法访问:

import Foundation
import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    var mapView: MKMapView!
    var problemChild: Int!

    override func viewWillDisappear(_ animated: Bool) {
        print("the view disappeared")
    }

    override func loadView() {

        mapView = MKMapView()

        view = mapView

        mapView.delegate = self

        mapView.isPitchEnabled = true

       // let atlLongLat = MKCoordinateRegion.init(center: CLLocationCoordinate2D.init(latitude: CLLocationDegrees.init(33.7490), longitude: CLLocationDegrees.init(84.3880)), span: MKCoordinateSpan.init(latitudeDelta: 33.7490, longitudeDelta: 84.3880))

        //mapView.setRegion(atlLongLat, animated: true)

        mapView.showsPointsOfInterest = true
        mapView.showsBuildings = true
        mapView.showsCompass = true
        mapView.showsTraffic = true
        let locationManager = CLLocationManager()
        locationManager.delegate = self
        let locationAuthStatus = CLLocationManager.authorizationStatus()
        if locationAuthStatus == .notDetermined {
            locationManager.requestWhenInUseAuthorization()
        }
        mapView.showsUserLocation = true


        let segmentedControl = UISegmentedControl.init(items: ["Standard", "Hybrid", "Satellite"])
        segmentedControl.selectedSegmentIndex = 0
        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
        segmentedControl.backgroundColor = UIColor.yellow
        view.addSubview(segmentedControl)

        let zoomButtonFrame = CGRect.init(x: 0, y: 0, width: view.bounds.width, height: 400)
        let zoomButton = UIButton.init(frame: zoomButtonFrame)
        zoomButton.backgroundColor = UIColor.green
        zoomButton.setTitle("Where Am I?", for: .normal)
        zoomButton.setTitleColor(UIColor.black, for: .normal)
        zoomButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(zoomButton)

        let guide = view.safeAreaLayoutGuide
        let topConstraint = segmentedControl.topAnchor.constraint(equalTo: guide.topAnchor, constant: 8)
        let zoomButtonTopConstraint = zoomButton.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 559)
        let margins = view.layoutMarginsGuide
        let zoomButtonLeadingConstraint = zoomButton.leadingAnchor.constraint(equalTo: margins.leadingAnchor)
        let leadingConstraint = segmentedControl.leadingAnchor.constraint(equalTo: margins.leadingAnchor)
        let trailingConstraint = segmentedControl.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
        let zoomButtonTrailingConstraint = zoomButton.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
        topConstraint.isActive = true
        leadingConstraint.isActive = true
        trailingConstraint.isActive = true
        zoomButtonTopConstraint.isActive = true
        zoomButtonLeadingConstraint.isActive = true
        zoomButtonTrailingConstraint.isActive = true


        segmentedControl.addTarget(self, action:#selector(mapTypeChanged(segControl:)), for: .valueChanged)

        zoomButton.addTarget(self, action: #selector(zoomButtonTapped(zoomButt:)), for: .touchUpInside)
    }

    @objc func mapTypeChanged(segControl: UISegmentedControl) {
        switch segControl.selectedSegmentIndex {
        case 0:
            mapView.mapType = .standard
        case 1:
            mapView.mapType = .mutedStandard
        case 2:
            mapView.mapType = .satelliteFlyover
        default:
            break
        }
    }

    @objc func zoomButtonTapped(zoomButt: UIButton){
        let b: Int = problemChild
        print(b)

        for _ in 1...5 {
            print("Pinging Your Location...")
            if zoomButt.backgroundColor == UIColor.green{
                print("this button's background color is green man.")
            }
        }

    }

    func mapViewWillStartLocatingUser(_ mapView: MKMapView) {
        //adding this here to get used to the idea of protocols
    }


}

提前谢谢你,我很抱歉听起来像个傻瓜,但我真的很想理解。

变量的范围是由其定义而不是赋值设置的
mapView
MapViewController
的属性。因此,它可以在
MapViewController
中的任何地方访问。这与分配时间无关

视图控制器有点不寻常,因为它们通常是从故事板初始化的,但有些片段在
viewDidLoad
之前无法初始化(因为它们引用故事板中的片段)。也就是说,这不是最好的代码。这样写会更好:

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    let mapView = MKMapView()
    ...
并且应该从
loadView
中删除行
mapView=MKMapView()
。它的书写方式是有效的,但它并不像它应该的那样清晰或安全。(
loadView
在视图控制器的生命周期中只调用一次。这并不总是正确的,但在Swift出现的时间内,这种情况一直存在。)


当我说这不是“应有的安全性”时,我的意思是,如果某个东西在
init
loadView
之间访问
mapView
(这可能由于各种原因而发生),这将崩溃。通过仔细管理,您可以避免这种情况,但避免
更安全可以键入时键入。

变量的范围由其定义设置,而不是由其赋值设置
mapView
MapViewController
的属性。因此,它可以在
MapViewController
中的任何地方访问。这与分配时间无关

视图控制器有点不寻常,因为它们通常是从故事板初始化的,但有些片段在
viewDidLoad
之前无法初始化(因为它们引用故事板中的片段)。也就是说,这不是最好的代码。这样写会更好:

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    let mapView = MKMapView()
    ...
并且应该从
loadView
中删除行
mapView=MKMapView()
。它的书写方式是有效的,但它并不像它应该的那样清晰或安全。(
loadView
在视图控制器的生命周期中只调用一次。这并不总是正确的,但在Swift出现的时间内,这种情况一直存在。)


当我说这不是“应有的安全性”时,我的意思是,如果某个东西在
init
loadView
之间访问
mapView
(这可能由于各种原因而发生),这将崩溃。通过仔细管理,您可以避免这种情况,但避免
更安全可以的时候键入。

在函数之外声明的变量,如
mapView
是实例变量。变量的作用域是实例可用的所有代码,因此可以从其他函数引用对象


通过在
loadView
内部初始化,对象引用仅在该赋值执行后才有效,但这与变量的可见性不同。

mapView
方式之外声明的变量是实例变量。变量的作用域是实例可用的所有代码,因此可以从其他函数引用对象

通过在
loadView
中初始化它,对象引用仅在该赋值执行后有效,但这与变量的可见性不同。

您询问:

在函数之外实例化对象,这样我们就可以在编写viewController时引用同一个对象,这不是最好的做法吗

如果你问“我们不喜欢属性而不是局部变量”的问题,答案是“不”。在防御性编程中,我们倾向于使用局部变量,但在实际需要视图控制器级别作用域的情况下除外,在这种情况下,我们使用属性。局部变量没有意外的数据共享/变异。属性,因为它们在所有方法中是共享的,所以它可能会在其他地方被意外更改(当然是适度的)。现在,如果您需要在各种方法中引用对象,例如使用
mapView
的情况,则需要一个属性。但是如果我们不需要在其他地方引用它,比如
locationManager
,那么如果可以,我们就坚持使用局部变量

如果不是,那么在ViewController顶部推断变量,然后在函数中实例化对象的目的是什么

首先,我们不是在视图控制器的顶部“推断变量”。我们只是“声明属性”,声明无论最终在何处实例化,都可以在整个视图控制器中访问该属性

关于预先声明属性的做法,但只是在稍后实例化对象并在类似
viewDidLoad
的函数中设置属性(或者在您的示例中,在
loadView
中),这并不是不合理的做法。它使实例化和配置保持一致
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    var mapView: MKMapView!

    ...


    override func loadView() {
        mapView = MKMapView()
        view = mapView
        ...
        let locationManager = CLLocationManager()
        ...
    }
}
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    @IBOutlet var mapView: MKMapView!

    ...


    override func viewDidLoad() {
        super.viewDidLoad()

        // perhaps reference the map view created in the storyboard to configure it, e.g.

        mapView.isPitchEnabled = true

        ...
        let locationManager = CLLocationManager()
        ...
    }
}