在类中保留循环导致潜在的内存泄漏

时间:2018-02-05 11:35:07

标签: ios swift memory-leaks

因此,使用图形调试工具和工具,我注意到我的应用程序中存在内存泄漏。经过几个小时后,我或者图形调试器已将其跟踪到此功能。

override func didUpdate(to object: Any) {
    comment = object as? CommentGrabbed
}

这个更大的课程包含哪个

import UIKit
import IGListKit
import Foundation
import Firebase

protocol CommentsSectionDelegate: class {
    func CommentSectionUpdared(sectionController: CommentsSectionController)
}
class CommentsSectionController: ListSectionController,CommentCellDelegate {
    weak var delegate: CommentsSectionDelegate? = nil
   weak var comment: CommentGrabbed?
    let userProfileController = ProfileeViewController(collectionViewLayout: UICollectionViewFlowLayout())
    var eventKey: String?
    var dummyCell: CommentCell?
    override init() {
        super.init()
        // supplementaryViewSource = self
        //sets the spacing between items in a specfic section controller
        inset = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0)
    }
    // MARK: IGListSectionController Overrides
    override func numberOfItems() -> Int {
        return 1
    }
    override func sizeForItem(at index: Int) -> CGSize {
        let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
        dummyCell = CommentCell(frame: frame)
        dummyCell?.comment = comment
        dummyCell?.layoutIfNeeded()
        let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
        let estimatedSize = dummyCell?.systemLayoutSizeFitting(targetSize)
        let height = max(40+8+8, (estimatedSize?.height)!)
        return  CGSize(width: collectionContext!.containerSize.width, height: height)

    }

    override var minimumLineSpacing: CGFloat {
        get {
            return 0.0
        }
        set {
            self.minimumLineSpacing = 0.0
        }
    }

    override func cellForItem(at index: Int) -> UICollectionViewCell {
        guard let cell = collectionContext?.dequeueReusableCell(of: CommentCell.self, for: self, at: index) as? CommentCell else {
            fatalError()
        }
        //  print(comment)
        cell.comment = comment
        cell.delegate = self
        return cell
    }
    override func didUpdate(to object: Any) {
        comment = object as? CommentGrabbed
    }
    override func didSelectItem(at index: Int){
    }

    func optionsButtonTapped(cell: CommentCell){
        print("like")

        let comment = self.comment
        _ = comment?.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment?.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment!)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let replyAction = UIAlertAction(title: "Reply to Comment", style: .default, handler: { (_) in
                //do something here later to facilitate reply comment functionality
                print("Attempting to reply to user \(comment?.user.username) comment")

            })
            alertController.addAction(replyAction)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment!, (comment?.eventKey)!)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
                self.onItemDeleted()

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        self.viewController?.present(alertController, animated: true, completion: nil)

    }
    func onItemDeleted() {
        delegate?.CommentSectionUpdared(sectionController: self)
    }
    func handleProfileTransition(tapGesture: UITapGestureRecognizer){
        userProfileController.user = comment?.user
        if Auth.auth().currentUser?.uid != comment?.uid{
                    self.viewController?.present(userProfileController, animated: true, completion: nil)
        }else{
            //do nothing

        }


    }

    deinit {
        print("CommentSectionController class removed from memory")
    }


}

CommentGrabbed类看起来像这样

import Foundation
import  IGListKit

class CommentGrabbed {
    let content: String
    let uid: String
    let user: User
    let creationDate: Date
    var commentID: String? = ""
    let eventKey:String

    init(user: User, dictionary: [String:Any]) {
        self.content = dictionary["content"] as? String ?? ""
        self.uid = dictionary["uid"] as? String ?? ""
        self.eventKey = dictionary["eventKey"] as? String ?? ""
        self.user = user
        let secondsFrom1970 = dictionary["timestamp"] as? Double ?? 0
        self.creationDate = Date(timeIntervalSince1970: secondsFrom1970)
    }

}

extension CommentGrabbed: Equatable{
    static public func  ==(rhs: CommentGrabbed, lhs: CommentGrabbed) ->Bool{
        return rhs.commentID == lhs.commentID
    }
}

extension CommentGrabbed: ListDiffable{
    public func diffIdentifier() -> NSObjectProtocol {
        return commentID! as NSObjectProtocol
    }
    public func isEqual(toDiffableObject object: ListDiffable?) ->Bool{
        guard let object = object as? CommentGrabbed else {
            return false
        }
        return  self.commentID==object.commentID
    }
}

这里是否有任何保留周期我不会注意到? 该控制器最终控制显示所有注释,并且它具有一个UserService方法,该方法可以通过此代码库进行操作

static func show(forUID uid: String, completion: @escaping (User?) -> Void) {
    let ref = Database.database().reference().child("users").child(uid)
    ref.observeSingleEvent(of: .value, with: { (snapshot) in
        guard let user = User(snapshot: snapshot) else {
            return completion(nil)

        }

        completion(user)
    })
}

User模型对象如下所示

import Foundation
import FirebaseDatabase.FIRDataSnapshot

class User : NSObject {
    //User variables
    let uid : String
    let username : String?
    let profilePic: String?
    var isFollowed = false
    var dictValue: [String : Any] {
        return ["username" : username as Any,
                "profilePic" : profilePic as Any]
    }
    //Standard User init()
    init(uid: String, username: String, profilePic: String) {
        self.uid = uid
        self.username = username
        self.profilePic = profilePic
        super.init()
    }
    //User init using Firebase snapshots
    init?(snapshot: DataSnapshot) {
        guard let dict = snapshot.value as? [String : Any],
            let username = dict["username"] as? String,
            let profilePic = dict["profilePic"] as? String
            else { return nil }
        self.uid = snapshot.key
        self.username = username
        self.profilePic = profilePic
    }
    //UserDefaults
    required init?(coder aDecoder: NSCoder) {
        guard let uid = aDecoder.decodeObject(forKey: "uid") as? String,
            let username = aDecoder.decodeObject(forKey: "username") as? String,
            let profilePic = aDecoder.decodeObject(forKey: "profilePic") as? String            else { return nil }
        self.uid = uid
        self.username = username
        self.profilePic = profilePic
        super.init()
    }
    init?(key: String, postDictionary: [String : Any]) {
        //var dict : [String : Any]
        //print(postDictionary as? [String:])
        let dict = postDictionary
        //print(dict)
        let profilePic = dict["profilePic"] as? String ?? ""
        let username = dict["username"] as? String ?? ""
        self.uid = key
        self.profilePic = profilePic
        self.username = username
    }
    //User singleton for currently logged user
    private static var _current: User?
    static var current: User {
        guard let currentUser = _current else {
            fatalError("Error: current user doesn't exist")
        }
        return currentUser
    }

    class func setCurrent(_ user: User, writeToUserDefaults: Bool = true) {
        //print(user)
       // print("")
        if writeToUserDefaults {
            let data = NSKeyedArchiver.archivedData(withRootObject: user)

            UserDefaults.standard.set(data, forKey: "currentUser")
            UserDefaults.standard.synchronize()
        }
        _current = user
        //print(_current ?? "")
    }
}

extension User: NSCoding {
    func encode(with aCoder: NSCoder) {
        aCoder.encode(uid, forKey: "uid")
        aCoder.encode(username, forKey: "username")
        aCoder.encode(profilePic, forKey: "profilePic")        
    }
}

当我将commentGrabbed类中的用户更改为弱时,如果必须处理与用户相关的任何内容,我的代码就会开始崩溃

 weak var comment: CommentGrabbed?{
        didSet{
            guard let comment = comment else{
                return
            }
          //  print("apples")
            // textLabel.text = comment.content
            //shawn was also here
            profileImageView.loadImage(urlString: (comment.user?.profilePic!)!)
            //  print(comment.user.username)
            let attributedText = NSMutableAttributedString(string: (comment.user?.username!)!, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])

            attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))

            attributedText.append(NSAttributedString(string: "\n\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 4)]))
            let timeAgoDisplay = comment.creationDate.timeAgoDisplay()
            attributedText.append(NSAttributedString(string: timeAgoDisplay, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12), NSAttributedStringKey.foregroundColor: UIColor.gray]))

            textView.attributedText = attributedText


        }
    }

其中commentGrabbed为init的代码块

 UserService.show(forUID: uid, completion: { [weak self](user) in
                    if let user = user {
                        let commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary)
                        commentFetched.commentID = snapshot.key
                        let filteredArr = self?.comments.filter { (comment) -> Bool in
                            return comment.commentID == commentFetched.commentID
                        }
                        if filteredArr?.count == 0 {
                            self?.comments.append(commentFetched)

                        }
                        self?.adapter.performUpdates(animated: true)
                    }else{
                        print("user is null")

                    }
                    self?.comments.sort(by: { (comment1, comment2) -> Bool in
                        return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending
                    })
                    self?.comments.forEach({ (comments) in
                    })
                })

            })

这是我在调试工具中看到的。如果我注释掉那条线,对于那些想知道为什么不发表评论的人来说,什么都没有加载 enter image description here

CommentCell代码按要求

import Foundation
import UIKit
import Firebase

protocol CommentCellDelegate: class {
    func optionsButtonTapped(cell: CommentCell)
    func handleProfileTransition(tapGesture: UITapGestureRecognizer)
}
class CommentCell: UICollectionViewCell {
    weak var delegate: CommentCellDelegate? = nil
    override var reuseIdentifier : String {
        get {
            return "cellID"
        }
        set {
            // nothing, because only red is allowed
        }
    }
    var didTapOptionsButtonForCell: ((CommentCell) -> Void)?

    weak var comment: CommentGrabbed?{
        didSet{
            guard let comment = comment else{
                return
            }
          //  print("apples")
            // textLabel.text = comment.content
            //shawn was also here
            profileImageView.loadImage(urlString: (comment.user?.profilePic!)!)
            //  print(comment.user.username)
            let attributedText = NSMutableAttributedString(string: (comment.user?.username!)!, attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])

            attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))

            attributedText.append(NSAttributedString(string: "\n\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 4)]))
            let timeAgoDisplay = comment.creationDate.timeAgoDisplay()
            attributedText.append(NSAttributedString(string: timeAgoDisplay, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12), NSAttributedStringKey.foregroundColor: UIColor.gray]))

            textView.attributedText = attributedText


        }
    }

    lazy var textView: UITextView = {
        let textView = UITextView()
        textView.font = UIFont.systemFont(ofSize: 14)
        textView.isScrollEnabled = false
        textView.textContainer.maximumNumberOfLines = 0
        textView.textContainer.lineBreakMode = .byCharWrapping
        textView.isEditable = false
        return textView
    }()

    lazy var profileImageView: CustomImageView = {
        let iv = CustomImageView()
        iv.clipsToBounds = true
        iv.isUserInteractionEnabled = true
        iv.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleProfileTransition)))
        iv.contentMode = .scaleAspectFill
        return iv
    }()

    lazy var flagButton: UIButton = {
        let flagButton = UIButton(type: .system)
        flagButton.setImage(#imageLiteral(resourceName: "icons8-Info-64"), for: .normal)
        flagButton.addTarget(self, action: #selector(optionsButtonTapped), for: .touchUpInside)
        return flagButton
    }()

    @objc func optionsButtonTapped (){
        didTapOptionsButtonForCell?(self)
    }

    @objc func onOptionsTapped() {
        delegate?.optionsButtonTapped(cell: self)
    }
    @objc func handleProfileTransition(tapGesture: UITapGestureRecognizer){
        delegate?.handleProfileTransition(tapGesture: tapGesture)
      //  print("Tapped image")
    }



    override init(frame: CGRect){
        super.init(frame: frame)
        addSubview(textView)
        addSubview(profileImageView)
        addSubview(flagButton)
        textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: flagButton.leftAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)
        profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
        profileImageView.layer.cornerRadius = 40/2
        flagButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 40, height: 40)
        flagButton.addTarget(self, action: #selector(CommentCell.onOptionsTapped), for: .touchUpInside)

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }




}

sizeForItem

override func sizeForItem(at index: Int) -> CGSize {
    let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
      weak var dummyCell = CommentCell(frame: frame)
    dummyCell?.comment = comment
    dummyCell?.layoutIfNeeded()
    let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
    let estimatedSize = dummyCell?.systemLayoutSizeFitting(targetSize)
    let height = max(40+8+8, (estimatedSize?.height)!)
    return  CGSize(width: collectionContext!.containerSize.width, height: height)

}

0 个答案:

没有答案