Grumme
Grumme

Reputation: 806

Memory issue from UITable

My app is crashing because of a memory issue (Too much memory used). It happens when i open enough of the objects from the TableView. I has searched the internet for the problem, and i can see it might be because the memory never is released. But how do i release it?

Here is my code.

TableExerciseViewController

class TableExercisesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var choosenCellIndex = 0
var filteredExercises = [Exercise]()

let searchController = UISearchController(searchResultsController: nil)

@IBOutlet weak var searchBar: UISearchBar!

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    searchController.searchResultsUpdater = self
    searchController.dimsBackgroundDuringPresentation = false

    searchController.searchBar.placeholder = "Look up an exercise..."

    definesPresentationContext = true
    tableView.tableHeaderView = searchController.searchBar
}

override func viewDidAppear(animated: Bool) {
    if let row = tableView.indexPathForSelectedRow {
        self.tableView.deselectRowAtIndexPath(row, animated: false)
    }
}


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




func filterContentForSearchText(searchText: String, scope: String = "All") {
    filteredExercises = ExerciseManager.sharedInstance.getExercises().filter {
        exercise in return exercise.name.lowercaseString.containsString(searchText.lowercaseString)
    }

    tableView.reloadData()
}


//Returns the size of the arraylist
 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {


    if searchController.active && searchController.searchBar.text != "" {
        return filteredExercises.count
    }
    return ExerciseManager.sharedInstance.getExercises().count
}

//Returns the selected Cell
 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("Exercise Cell", forIndexPath: indexPath) as! ExerciseCustomCell
    let exercise: Exercise
    if searchController.active && searchController.searchBar.text != "" {
        exercise = filteredExercises[indexPath.row]
    } else {
        exercise = ExerciseManager.sharedInstance.getExercises()[indexPath.row]
    }

    cell.imgExercise?.image = exercise.exerciseImage1
    cell.lblExerciseName?.text = exercise.name
    cell.lblExerciseMuscleGroup?.text = exercise.muscle
    cell.lblExerciseTools?.text = "Equipment: \(exercise.tool)"

    return cell
}

 func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}


//Sends the data through the selected identifier
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    choosenCellIndex = indexPath.row

    // Start segue with index of cell clicked
    self.performSegueWithIdentifier("Show Exercise", sender: self)

}

//Prepares the data for the segue and the next viewcontroller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    // get a reference to the second view controller
    let exerciseViewController = segue.destinationViewController as! ExerciseViewController

    // set a variable in the second view controller with the data to pass
    if searchController.active && searchController.searchBar.text != "" {
        exerciseViewController.exercise = filteredExercises[choosenCellIndex]
    } else {
        exerciseViewController.exercise = ExerciseManager.sharedInstance.getExercises()[choosenCellIndex]
    }

}

deinit {
    debugPrint("TableExerciseView deinitialized...")
}

}

extension TableExercisesViewController: UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
    filterContentForSearchText(searchController.searchBar.text!)
}

ExerciseViewController

class ExerciseViewController: UIViewController {
weak var exercise: Exercise?
var receivedCellIndex = 0

@IBOutlet weak var scrollView: UIScrollView!

@IBOutlet weak var lblExerciseName: UILabel!

@IBOutlet weak var lblExerciseMuscleGroup: UILabel!

@IBOutlet weak var lblStartDescription: UILabel!

@IBOutlet weak var lblLastDescription: UILabel!


@IBOutlet weak var imgExerciseAnimation: UIImageView!

@IBOutlet weak var imgExerciseStartPosition: UIImageView!

@IBOutlet weak var imgExerciseLastPosition: UIImageView!

var timer = NSTimer()
var counter = 1


override func viewDidLoad() {
    super.viewDidLoad()

    //Sets the color of the back button in navigation bar
    self.navigationController!.navigationBar.tintColor = UIColor.whiteColor();

    //Sets the labels
    lblExerciseName.text = exercise?.name
    lblExerciseMuscleGroup.text = exercise!.muscle
    lblStartDescription.text = "1. \(exercise!.startDescription)"
    lblLastDescription.text = "2. \(exercise!.lastDescription)"


    //Sets the antimation and the images
    imgExerciseAnimation.image = exercise?.exerciseImage1
    imgExerciseStartPosition.image = exercise?.exerciseImage1
    imgExerciseLastPosition.image = exercise?.exerciseImage2

    //Timer
    self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(ExerciseViewController.demonstrateExercise), userInfo: nil, repeats: true)

}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.timer.invalidate()
}

func demonstrateExercise() {
    if counter == 1 {
        imgExerciseAnimation.image = exercise?.exerciseImage2
        counter += 1
    } else {
        counter = 1
        imgExerciseAnimation.image = exercise?.exerciseImage1
    }
}
 }

Upvotes: 0

Views: 156

Answers (2)

Cherpak Evgeny
Cherpak Evgeny

Reputation: 2770

Override deinit function - and check if its called when you exit the Exercise view. If not - i would destroy the timer in viewWillDisappear or Did functions.

Upvotes: 0

Jeremy Rea
Jeremy Rea

Reputation: 454

I'm guessing that what's taking up the memory are the exercise images in your ExerciseViewController. Since Swift uses ARC to manage memory, you'll want to remove references to those images (and exercice for that matter) so that the system can release them from memory.

override func viewDidDisappear(animated: Bool) {
    lblExerciseName.text = nil
    lblExerciseMuscleGroup.text = nil
    lblStartDescription.text = nil
    lblLastDescription.text = nil

    imgExerciseAnimation.image = nil
    imgExerciseStartPosition.image = nil
    imgExerciseLastPosition.image = nil
}

Since your exercice variable is weak, and nothing is referencing to it, the system will now free it from memory instead of having multiple copies of exercices build up every time the user visits a new ExerciseViewController.

Upvotes: 1

Related Questions