UICollectionViewCell receives "UICollectionViewCell message sent to deallocated instance" right after being dequeued with the method collectionView.dequeueReusableCellWithReuseIdentifier

Original question was posted on stack overflow here: http://stackoverflow.com/questions/35076277/uicollectionviewcell-message-sent-to-deallocated-instance-right-after-being-dequ


I have a UICollectionViewCell which is being dequeued for reuse. I recently introduced a sliding drawer functionality which is basically a hidden view which gets displayed when the user swipes.



After the last change this seemed to create a case where the instance was being deallocated while being initialized. The exact location where the Zombie was being reported was on the call to "addGestureRecognizers()" which was being called from setupSlidingDrawer() which was in turn being called from the init function.



I have attached an image of the break point before and after the Zombie call. I have been pulling my hair all day looking at this and keep drawing a blank.



As a work around (hack) I have made the deleteView a instance variable and that seems to keep the class from releasing as the retain count is not decremented.



I feel like like the code for my collection view cell / controller is pretty standard and I am not doing anything crazy.



This seems like it is a Apple bug but I can not seem to find anything on the inter webs to prove it. If




class MomentsViewCell : UICollectionViewCell, UIGestureRecognizerDelegate {

static let reuseIdentifier = "MomentsViewCell"

let imageView = UIImageView()

let activityIndicator = UIActivityIndicatorView()

let shareButton = UIButton()

var shareCallback: (() -> ())?

var isSwiped = false

let overlayViewTag = 999

var delegate: MomentsViewController?

let shareButtonLength: CGFloat = 44

lazy var drawerWidth: CGFloat = {

self.frame.width * 0.15

}()

override init(frame: CGRect) {

super.init(frame: frame)

contentView.addSubview(imageView)

imageView.contentMode = UIViewContentMode.ScaleAspectFit

imageView.snp_makeConstraints { (make) -> Void in

make.centerX.equalTo(self.contentView.snp_centerX)

make.width.equalTo(self.contentView.snp_width)

make.top.equalTo(self.snp_top)

make.bottom.equalTo(self.snp_bottom)

}

contentView.addSubview(activityIndicator)

activityIndicator.hidesWhenStopped = true

activityIndicator.snp_makeConstraints { (make) -> Void in

make.center.equalTo(self.snp_center)

}

let shareImage = UIImage(named: "share-moment")

shareButton.setImage(shareImage, forState: .Normal)

shareButton.addTarget(self, action: "didTapShareButton", forControlEvents: .TouchUpInside)

shareButton.enabled = false

shareButton.alpha = 0.0

imageView.addSubview(shareButton)

imageView.userInteractionEnabled = true

shareButton.snp_makeConstraints { (make) -> Void in

let size = CGSizeMake(shareButtonLength, shareButtonLength)

var offset = shareButtonLength / 2

if let imageLength = shareImage?.size.height {

offset = (shareButtonLength - imageLength) / 2

}

make.bottom.equalTo(self.imageView).multipliedBy(0.95).offset(offset)

make.right.equalTo(self.imageView).multipliedBy(0.95).offset(offset)

make.size.equalTo(size)

}

setupSlidingDrawer()

}

required init?(coder aDecoder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

override func prepareForReuse() {

super.prepareForReuse()

imageView.image = nil

exitDeleteMode()

}

func enableSharing() {

shareButton.enabled = true

shareButton.alpha = 1.0

}

func didTapShareButton() {

shareCallback?()

}



func setupSlidingDrawer() {

let deleteView = UIView(frame: CGRectMake(self.frame.width - drawerWidth, self.frame.origin.y, drawerWidth, self.frame.height))

deleteView.backgroundColor = UIColor.whiteColor()

let deleteGradientLayer = CAGradientLayer()

deleteGradientLayer.frame = deleteView.bounds

deleteGradientLayer.colors = [UIColor(hex: 0xE8557C).CGColor, UIColor(hex: 0xCC2B49).CGColor]

deleteView.layer.insertSublayer(deleteGradientLayer, atIndex: 0)

let trashButton = UIButton()

trashButton.setImage(UIImage(named: "trash_can"), forState: UIControlState.Normal)

trashButton.addTarget(self, action: Selector("didPressDelete"), forControlEvents: .TouchUpInside)

deleteView.addSubview(trashButton)

deleteView.bringSubviewToFront(trashButton)

trashButton.snp_makeConstraints(closure: { (make) -> Void in

make.center.equalTo(deleteView.snp_center)

make.width.equalTo(deleteView.snp_width).multipliedBy(0.354)

make.height.equalTo(deleteView.snp_height).multipliedBy(0.07)

})

contentView.addSubview(deleteView)

deleteView.snp_makeConstraints(closure: { (make) -> Void in

make.left.equalTo(self.imageView.snp_right)

make.height.equalTo(self.contentView.snp_height)

make.width.equalTo(drawerWidth)

})

addGestureRecognizers()

}

func addGestureRecognizers() {

let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("didSwipeCell:"))

swipeGestureRecognizer.delegate = self

swipeGestureRecognizer.direction = .Left

self.addGestureRecognizer(swipeGestureRecognizer)

}





class MomentsViewController: ContainerSubController, UICollectionViewDelegate, UICollectionViewDataSource {

var moments: [Moment] = []

let layout = UICollectionViewFlowLayout()

var collectionView = UICollectionView(frame: CGRectZero, collectionViewLayout: UICollectionViewFlowLayout())

let headerIdentifier = "headerCellIdentifier"

let headerLabelText = "Moments"

let headerCellHeight: CGFloat = 103.0

let noMomentsLabelText = "It's looking pretty empty in here! Assign a Moments action (Share Moment To Twitter) to a Hot Key to get started."

let noMomentsReuseIdentifier = "NoMoments"

var currentSwipedCellIndexPath: NSIndexPath?



override func viewDidLoad() {

super.viewDidLoad()

layout.minimumLineSpacing = 0

collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)

collectionView.showsVerticalScrollIndicator = false

collectionView.backgroundColor = UIColor.clearColor()

collectionView.registerClass(MomentsViewCell.self, forCellWithReuseIdentifier: MomentsViewCell.reuseIdentifier)

collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: noMomentsReuseIdentifier)

collectionView.registerClass(UICollectionViewCell.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerIdentifier)

collectionView.delegate = self

collectionView.dataSource = self

view.backgroundColor = UIColor.blackColor()

view.addSubview(collectionView)

collectionView.snp_makeConstraints { (make) -> Void in

make.height.equalTo(view)

make.width.equalTo(view)

make.center.equalTo(view)

}

setupTransitionToMainButton()

}

override func viewWillAppear(animated: Bool) {

super.viewWillAppear(animated)

refreshMoments()

collectionView.reloadData()

NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleMomentSavedNotifcation", name: MomentNotifcation.MomentSaved.rawValue, object: nil)

}

//MARK: UICollectionViewDataSource

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

guard let momentsViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(MomentsViewCell.reuseIdentifier, forIndexPath: indexPath) as? MomentsViewCell, moment = moments[safe:indexPath.row] else {

return MomentsViewCell()

}



[![enter image description here][1]][1]





[![enter image description here][2]][2]





[1]: http://i.stack.imgur.com/q3ztY.png

[2]: http://i.stack.imgur.com/Uoxg3.jpg

UICollectionViewCell receives "UICollectionViewCell message sent to deallocated instance" right after being dequeued with the method collectionView.dequeueReusableCellWithReuseIdentifier
 
 
Q