ios - 滑动图像数组时出现内存泄漏

标签 ios swift xcode memory-leaks

我目前正在开发一个应用程序,并意识到存在内存泄漏。当您查看下面的 SecondViewController 类时,您将看到该应用程序允许用户在“Card”类型的数组中左右滑动。 “Card”具有图像属性,每次用户向左或向右滑动时都会显示该图像。我发现每次用户滑动到新照片时,使用的内存量都会增加。我不知道这是怎么发生的。当加载 SecondViewController 类时,会在 viewDidLoad 中形成“Card”数组。我们所做的就是每次滑动时引用“Card”数组。我不确定我的代码中导致内存泄漏的强引用在哪里。下面我将发布我的 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")
        }
    }
}

最佳答案

图像是昂贵的大型对象,当您调用 image(named:) 时,系统会缓存它们,并且在必要时不会释放它们。切勿制作一系列图像或包含图像的事物(例如您的卡片)。仅维护图像名称,仅在实际需要显示图像时才获取图像,并在不再显示图像时释放它,并且为了实现最快的非缓存访问,请勿使用以下命令获取实际图像图像(命名:)

还有一个提示:即使您确实获取了要显示的图像,也请将其缩小到显示所需的最小尺寸,以免在比您需要的更大的图像上浪费内存。

关于ios - 滑动图像数组时出现内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49970690/

相关文章:

ios - 几何入口点的 SceneKit 着色器修改器适用于 iOS 但不适用于 OS X

ios - 保存结构的最快方法 iOS/Swift

iphone - Info.plist 上传中的 CFBundleVersion 显示错误

ios - 如果创建 BLE 应用程序,我的应用程序应该支持哪个最低 iOS 版本?

swift - 返回对象比原始类型有什么好处?

swift - 如何在 OSX、Xcode 8、Swift 3 中实现单选按钮?

ios - 使用多个 segues 返回而不是展开是个坏主意吗?

ios - MonoTouch 绑定(bind)库 - EXC_BAD_ACCESS (SIGSEGV)

iphone - SKAction 在持续时间内显示单个图像?

ios - 弹出到 Root View Controller 时无法更新导航栏