在浏览图像阵列时内存泄漏

时间:2018-04-22 20:58:21

标签: ios swift xcode memory-leaks

我目前正在开发应用程序并意识到存在内存泄漏。当您查看下面的SecondViewController类时,您将看到该应用程序允许用户左右滑动一个类型为' Card'的数组。 '卡'具有图像属性,每次用户向左或向右滑动时都会显示该图像。我发现每次用户滑动到新照片时,使用的内存量都会增加。我不知道如何发生这种情况。加载SecondViewController类时,'卡' array在viewDidLoad中形成。我们所做的只是引用“卡片”和“卡片”。每次刷卡时阵列。我不确定导致内存泄漏的代码中强引用的位置。下面我将发布我的SecondViewController类和Card类。

import UIKit

class SecondViewController: UIViewController , UIGestureRecognizerDelegate, isOnProtocol {

    @IBAction func home(_ sender: Any) {
        performSegue(withIdentifier: "home", sender: self)
    }

    @IBOutlet weak var flashcardLabel: UILabel!
    @IBOutlet weak var imgPhoto: UIImageView!

    var imageIndex: Int = 0
    var itemList:[Card] = []

    func addlist(list:[String]) {
        for word in list {
            itemList.append(Card(image: UIImage(named: word)!, soundUrl: word))
        }
    }
    override func viewWillLayoutSubviews() {
        if (UserDefaults.standard.bool(forKey: "changed") == true) {
            view.bottomAnchor.constraint(equalTo: imgPhoto.bottomAnchor, constant: ((view.frame.size.height) * 0.17)).isActive = true
            flashcardLabel.topAnchor.constraint(equalTo: imgPhoto.bottomAnchor, constant: 5).isActive = true
        } else {
            flashcardLabel.isHidden = true
            view.bottomAnchor.constraint(equalTo: imgPhoto.bottomAnchor, constant: 20).isActive = true
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        for i in 0..<11 {
            let list: [String]

            switch i {
            case 0: list = ["lake", "lamb", "lamp", "lark", "leaf", "leash", "left", "leg", "lime", "lips", "list", "lock", "log", "look", "love", "lunch"]
            case 1: list = ["ladder", "ladybug", "laughing", "lawnmower", "lemon", "leopard", "leprechaun", "lion", "letters", "licking", "lifesaver", "lifting", "lightbulb", "lightning",
                            "listen", "llama"]
            case 2: list = ["alligator", "balance", "ballerina", "balloon", "bowling", "cello", "colors", "dollar", "elephant", "eyelashes", "family", "gasoline", "goalie", "hula", "jellyfish", "olive", "pillow", "pilot", "polar bear", "pelican", "ruler", "salad", "silly", "telephone", "television", "tulip", "umbrella", "valentine", "violin", "yellow", "xylophone"]
            case 3: list = ["apple", "ball", "bell", "bubble", "castle", "fall", "seal", "girl", "owl", "pail", "peel", "pool", "smile", "whale", "wheel"]
            case 4: list = ["planet", "plank", "plant", "plate", "play", "plum", "plumber", "plus"]
            case 5: list = ["black", "blanket", "blender", "blocks", "blond", "blood", "blow", "blue"]
            case 6: list = ["flag", "flip flop", "float", "floor", "flower", "fluffy", "flute", "fly"]
            case 7: list = ["glacier", "glad", "glasses", "glide", "glitter", "globe", "glove", "glue"]
            case 8: list = ["clam", "clamp", "clap", "claw", "clean", "climb", "clip", "cloud"]
            case 9: list = ["sled", "sleep", "sleeves", "slice", "slide", "slime", "slip", "slow"]
            case 10: list = ["belt", "cold", "elf", "gold", "golf", "melt", "milk", "shelf"]
            default: fatalError()
            }

            if UserDefaults.standard.value(forKey: "\(i)") as? Bool ?? true {
                addlist(list:list)
            }
        }

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
        imgPhoto.isUserInteractionEnabled = true
        imgPhoto.addGestureRecognizer(tapGestureRecognizer)

        imageDisplayed()

        // Do any additional setup after loading the view.
        //imgPhoto.isUserInteractionEnabled = true

        let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(Swiped(gesture:)))
        leftSwipe.cancelsTouchesInView = false

        let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(Swiped(gesture:)))
        rightSwipe.cancelsTouchesInView = false

        leftSwipe.direction = .left
        rightSwipe.direction = .right

        view.addGestureRecognizer(leftSwipe)
        view.addGestureRecognizer(rightSwipe)
    }

    @IBAction func memoryButton(_ sender: Any) {
        performSegue(withIdentifier: "memory", sender: self)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? MemoryViewController{
            destination.returnToImage = imageIndex
        }
    }

    internal func isOn() {
        imgPhoto.isUserInteractionEnabled = true
    }

    func Swiped(gesture: UIGestureRecognizer) {
        imgPhoto.isUserInteractionEnabled = true

        if let swipeGesture = gesture as? UISwipeGestureRecognizer {

            switch swipeGesture.direction {

            case UISwipeGestureRecognizerDirection.right:

                // decrease index first
                imageIndex -= 1

                // check if index is in range
                if imageIndex < 0 {
                    imageIndex = itemList.count - 1
                }

                UIImageView.transition(with: imgPhoto, duration: 0.3, options: .transitionFlipFromLeft, animations: nil, completion: nil)
                imgPhoto.image = itemList[imageIndex].image
                flashcardLabel.text = itemList[imageIndex].soundUrl

            case UISwipeGestureRecognizerDirection.left:

                // increase index first
                imageIndex += 1

                // check if index is in range
                if imageIndex > itemList.count - 1 {
                    imageIndex = 0
                }

                UIImageView.transition(with: imgPhoto, duration: 0.3, options: .transitionFlipFromRight, animations: nil, completion: nil)
                imgPhoto.image = itemList[imageIndex].image
                flashcardLabel.text = itemList[imageIndex].soundUrl

            default:
                break //stops the code/codes nothing.
            }
        }
    }

    func imageDisplayed() {
        imgPhoto.image = itemList[imageIndex].image
        flashcardLabel.text = itemList[imageIndex].soundUrl
    }

    func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
        imgPhoto.isUserInteractionEnabled = false

        let card = itemList[imageIndex]
        card.delegate = self
        card.playSound()
    }
}

卡类

import Foundation
import UIKit
import AVFoundation

protocol isOnProtocol {
    func isOn()
}

class Card: NSObject {
    var delegate: isOnProtocol!
    var player: AVAudioPlayer?
    var image: UIImage
    var soundUrl: String

    init(image: UIImage, soundUrl: String, isActive:Bool = true) {
        self.image = image
        self.soundUrl = soundUrl
    }

    func playSound() {
        guard let url = Bundle.main.url(forResource: self.soundUrl, withExtension: "m4a") else { return }

        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)

            player = try AVAudioPlayer(contentsOf: url)
            player?.delegate = self

            guard let player = player else { return }

            player.prepareToPlay()
            player.play()
            print("play")
        } catch let error {
            print(error.localizedDescription)
        }
    }
}

extension Card: AVAudioPlayerDelegate {

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool){
        if let del = delegate {
            del.isOn()
        } else {
            print("the delegate is not set")
        }
    }
}

1 个答案:

答案 0 :(得分:0)

图片是价格昂贵的大型对象,系统会在您致电image(named:)时对其进行缓存,并且不会放弃它们直到必须这样做。切勿制作包含图像的图像或事物(如卡片)。仅保留图像名称,仅在实际需要显示图像时获取图像,并在不再显示图像时释放图像,并且为了获得最快的非缓存访问权限,请不要获取实际图像使用image(named:)的图片。

还有一个提示:即使您提取图像进行显示,也要将其缩小到显示所需的最小尺寸,以免在更大的图像上浪费内存而不是您需要的内容。