簡體   English   中英

默認是自動釋放快速? 當應用程序空閑時,是什么導致內存增長?

[英]Is swift by default autorelease? What causes memory growth when the app is idle?

在調試此問題中的代碼時, UILabel在按鈕點擊后不會更新我注意到模擬的內存使用量不斷增長,即使我在調試器中而不是查看應用程序。

我修改了addButtonsAndLabels()函數來防止內存丟失,這是一個不好的做法嗎?

    func addButtonAndLabels() -> Void {
        // If the width of the screen hasn't been used as a base for the size of the sub-views then
        // this function is not ready to generate the sub-views.
        if (selfWidth < 1.0) {
            return;
        }
        var viewElementVerticalLocation: CGFloat = startingVerticalLocation;

        // To prevent memory leaks only create the UIView object if it hasn't already been created
        if (self.getGPSLongitudeAndLatitudeWithTimeStamp == nil) {
            self.getGPSLongitudeAndLatitudeWithTimeStamp = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get GPS Location with TimeStamp", underSubview: nil)            
            self.getGPSLongitudeAndLatitudeWithTimeStamp?.addTarget(self, action: #selector(setLabelWithGPSLatitudeAndLongitudeWithTimeStampData), for:  .touchUpInside)
            viewElementVerticalLocation += buttonVerticalSeparation
    }
    // ... more of the above ...
}

這段代碼是否干擾了ARC? 這段代碼不必要嗎?

我還注意到在init()之前調用了viewDidLoad() ,因為我在兩個函數中都有斷點。

如果這是重復,請指出我正確的方向。

編輯

import UIKit

class ViewController: UIViewController {
    var getGPSLongitudeAndLatitudeWithTimeStamp : UIButton?
    var getBatteryLevelAndState : UIButton?
    var getNextorkImplementation : UIButton?
    var displayButtonAction : UILabel?
    var displayDataModel : PCI7DataModelLibrary?

    // The following variables are used in multiple functions. They are constant during the display of the super view
    // and control the size of the subviews. They should change when the orientation changes
    var selfWidth : CGFloat = 0.0
    var buttonHeight : CGFloat = 0.0
    var viewElementWidth : CGFloat = 0.0
    var buttonYCenterOffset : CGFloat = 0.0       // The y center should be half the value of the height
    var buttonXCenter : CGFloat = 0.0             // Center the button title relative to the width of the button and the width of the super view
    var buttonXInit : CGFloat = 0.0
    var buttonVerticalSeparation : CGFloat = 0.0
    var startingVerticalLocation : CGFloat = 0.0
    var displayLabelHeight: CGFloat = 75.0

    // TODO: This function should be altered so that all values are calculated on screen height and screen width,
    //      this will allow for changes in orientation.
    func initFramingValuesOfMyDisplay() {
        selfWidth = self.view.bounds.size.width
        buttonHeight = 20.0               // This should be programmable in relative to self.view.bounds.size.height
        viewElementWidth = 0.8 * selfWidth;
        buttonXCenter = selfWidth / 2.0;   // Center the button title relative to the width of the button and the width of the super view
        buttonXInit = selfWidth * 0.1;      // 10 percent margin on the left leaves a 10% margin on the right as well
        buttonYCenterOffset = buttonHeight / 2.0; // The y center should be half the value of the height
        buttonVerticalSeparation = buttonHeight + buttonYCenterOffset;
        startingVerticalLocation = 430.0;  // 430 was chosen based on experimentation in the simulator
    }

    // This function is called when the getGPSLongitudeAndLatitudeWithTimeStamp button is receives the touchUpInside event.
    func setLabelWithGPSLatitudeAndLongitudeWithTimeStampData()
    {
        var actionString : String = "Testing Label Text"

        if (self.displayDataModel != nil) {
            actionString = (self.displayDataModel?.provideGPSLocationData())!
        }
        else {
            actionString = "GPS Button Action Failure: Data Model not created"
        }

        DispatchQueue.main.async {
            self.displayButtonAction?.text = nil
            self.displayButtonAction?.text = actionString
        }
    }

    // This function is called when the getBatteryLevelAndState button is receives the touchUpInside event.
    func setLabelWithBatteryLevelAndState() {
        var actionString : String = "Get Battery Level and State";

        if (self.displayDataModel != nil) {
            actionString = (self.displayDataModel?.provideBatteryLevelAndState())!
        }
        else {
            actionString = "Battery Button Action Failure: Data Model not created"
        }

        DispatchQueue.main.async {
            self.displayButtonAction?.text = nil
            self.displayButtonAction?.text = actionString
        }
    }

    // This function is called when the getNextorkImplementation button is receives the touchUpInside event.
    func setLabelActionNetwork() {
        var actionString :String = "Fake Button set to American Express Stock Price"

        if (self.displayDataModel != nil) {
            actionString = (self.displayDataModel?.provideNetworkAccessData())!
        }
        else {
            actionString = "Network Button Action Failure: Data Model not created"
        }

        DispatchQueue.main.async {
            self.displayButtonAction?.text = nil
            self.displayButtonAction?.text = actionString
        }
    }

    func makeAButton(yButtonStart : CGFloat, buttonTitle: String, underSubview: UIButton?) -> UIButton
    {
        let thisButton = UIButton.init(type: .system)
        thisButton.frame = CGRect(x: buttonXInit, y: yButtonStart, width: viewElementWidth, height: buttonHeight)
        thisButton.setTitle(buttonTitle, for:UIControlState.normal)
        thisButton.backgroundColor = UIColor.yellow
        thisButton.setTitleColor(UIColor.black, for: UIControlState.normal)

        if ((underSubview) == nil) {
            self.view.addSubview(thisButton)
        }
        else {
            self.view.insertSubview(thisButton, belowSubview:underSubview!)
        }

        return thisButton;
    }

    func makeALabel(yLabelStart : CGFloat, height: CGFloat, underSubview: UIButton?) -> UILabel
    {
        let thisLabel = UILabel.init()
        thisLabel.frame = CGRect(x: buttonXInit, y: yLabelStart, width: viewElementWidth, height: height)
                thisLabel.font = thisLabel.font.withSize(12)     // Reduce the size of the text so that more output fits on a single line
        thisLabel.lineBreakMode = .byWordWrapping;
        thisLabel.numberOfLines = 0;                          // Allow the label to grow as necessary
        thisLabel.textAlignment = NSTextAlignment.center;
        thisLabel.textColor = UIColor.black;

        if ((underSubview) == nil) {
            self.view.addSubview(thisLabel)
        }
        else {
            self.view.insertSubview(thisLabel, belowSubview:underSubview!)
        }

        return thisLabel;
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // rather than assume a particular background color, set the background color so that everything can be seen.
        self.view.backgroundColor = UIColor.white
        initFramingValuesOfMyDisplay()
        if (self.displayDataModel == nil) {
            self.displayDataModel = PCI7DataModelLibrary.init()
        }
        addButtonAndLabels()
    }

    func addButtonAndLabels() -> Void {
        // If the width of the screen hasn't been used as a base for the size of the sub-views then
        // this function is not ready to generate the sub-views.
        if (selfWidth < 1.0) {
            return;
        }
        var viewElementVerticalLocation: CGFloat = startingVerticalLocation;

        // To prevent memory leaks only create the UIView object if it hasn't already been created
        if (self.getGPSLongitudeAndLatitudeWithTimeStamp == nil) {
            self.getGPSLongitudeAndLatitudeWithTimeStamp = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get GPS Location with TimeStamp", underSubview: nil)
            self.getGPSLongitudeAndLatitudeWithTimeStamp?.addTarget(self, action: #selector(setLabelWithGPSLatitudeAndLongitudeWithTimeStampData), for:  .touchUpInside)
            viewElementVerticalLocation += buttonVerticalSeparation
        }

        if (self.getGPSLongitudeAndLatitudeWithTimeStamp == nil) {
            self.getBatteryLevelAndState = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get Battery Level and State", underSubview: getGPSLongitudeAndLatitudeWithTimeStamp)
            self.getBatteryLevelAndState?.addTarget(self, action: #selector(setLabelWithBatteryLevelAndState), for:  .touchUpInside)
            viewElementVerticalLocation += buttonVerticalSeparation
        }

        if (self.getNextorkImplementation == nil) {
            self.getNextorkImplementation = makeAButton(yButtonStart: viewElementVerticalLocation, buttonTitle: "Get American Express Stock Price", underSubview: getBatteryLevelAndState)
            self.getNextorkImplementation?.addTarget(self, action: #selector(setLabelActionNetwork), for:  .touchUpInside)
            viewElementVerticalLocation += buttonVerticalSeparation
        }


        if (self.displayButtonAction == nil) {
            self.displayButtonAction = makeALabel(yLabelStart: viewElementVerticalLocation, height: displayLabelHeight, underSubview: getNextorkImplementation)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    required init(coder aDecoder: NSCoder) {
        super.init(nibName: nil, bundle: nil)
        if (self.displayDataModel == nil) {
            self.displayDataModel = PCI7DataModelLibrary.init()
        }
        initFramingValuesOfMyDisplay()
    }

}

回答這個問題有點困難,因為我擔心你對Swift內存管理的工作原理以及iOS視圖控制器生命周期的工作方式有很多誤解。 他們誤解並不奇怪; 很難知道從哪里開始。 我可能會介紹你已經知道的事情。

首先,從研究Swift手冊的自動參考計數部分開始。 理解ARC的關鍵在於它基於強引用。 如果兩個東西之間有很強的引用,那就是一個保留循環,直到它被破壞,這兩個對象都不會被釋放。

問“默認自動釋放是否是Swift”並沒有什么意義。 自動釋放是一個稍微高級的概念,您可能不需要了解基本的Swift。 (我已經和它一起工作了很長時間,我希望我不會過分不屑於人們是否需要理解它,但我認為你現在可以避免考慮它。)自動釋放與你想要的對象有關存在於當前事件循環的其余部分,否則將保留零保留計數。 雖然它仍然存在於ARC中,但它主要用於手動保留計數,你很少需要在Swift中考慮太多,特別是作為初學者。

我還注意到在init()之前調用了viewDidLoad(),因為我在兩個函數中都有斷點。

在給定對象的init之前調用viewDidLoad是不可能的。 幾乎可以肯定,你的問題是這個對象不止一個,這讓你感到驚訝。 通常,如果您print(self) (或在調試器中使用p self ),它將顯示對象的地址。 如果它有不同的地址,則它是一個不同的對象。 您可能希望實現deinit以便在那里放置斷點並探索視圖控制器的生命周期。

我假設在這個按鈕上有一個addSubview調用。 幾乎可以肯定的是,您多次調用addButtonAndLabels (可能是因為您對視圖生命周期感到困惑),並且每次調用都會將按鈕添加到視圖中。 因此,您在視圖中獲得越來越多的按鈕,每個按鈕都占用內存。 如果您只需要一次,您需要確保只添加一次。 您可以在調試器中使用p view.subviews (或print(view.subviews) )查看此問題。 你可能會看到很多按鈕。 這不是泄漏。 這些只是您創建的對象,您並不打算創建它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM