我目前正在开发应用程序并意识到存在内存泄漏。当您查看下面的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")
}
}
}
答案 0 :(得分:0)
图片是价格昂贵的大型对象,系统会在您致电image(named:)
时对其进行缓存,并且不会放弃它们直到必须这样做。切勿制作包含图像的图像或事物(如卡片)。仅保留图像名称,仅在实际需要显示图像时获取图像,并在不再显示图像时释放图像,并且为了获得最快的非缓存访问权限,请不要获取实际图像使用image(named:)
的图片。
还有一个提示:即使您提取图像进行显示,也要将其缩小到显示所需的最小尺寸,以免在更大的图像上浪费内存而不是您需要的内容。