因此,使用图形调试工具和工具,我注意到我的应用程序中存在内存泄漏。经过几个小时后,我或者图形调试器已将其跟踪到此功能。
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
})
})
})
这是我在调试工具中看到的。如果我注释掉那条线,对于那些想知道为什么不发表评论的人来说,什么都没有加载
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)
}