我目前正在开发一个应用程序,并意识到存在内存泄漏。当您查看下面的 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/