#ios #swift #memory-leaks #delegates
#iOS #swift #утечки памяти #делегаты
Вопрос:
Я работаю с координаторами. Мой ViewController не освобождает выделение, даже если я установил слабые делегаты.
Координатор:
class JournalDetailCoordinator: Coordinator {
var dependencys: AppDependency
var navigationController: UINavigationController
var collectionViewController: CollectionViewWithMenuController!
var imagePickerManager: ImagePickerManager!
init(dependencys: AppDependency, navigationController: UINavigationController) {
self.dependencys = dependencys
self.navigationController = navigationController
}
func start() {
loadCollectionViewController()
}
deinit {
print("JournalDetailCoordinator deinitialisiert")
}
func loadCollectionViewController() {
var journalDetailViewControllerContainer = [JournalDetailViewController]()
for journal in dependencys.journals {
let vc: JournalDetailViewController = dependencys.getJournalDetailDependency().createVC()
vc.entryJournal = journal
vc.delegateLoadImagePickerManager = self
journalDetailViewControllerContainer.append(vc)
}
collectionViewController = dependencys.getCollectionViewWithMenuDependency().createVC()
collectionViewController.managedViewControllers = journalDetailViewControllerContainer
navigationController.pushViewController(collectionViewController, animated: true)
}
}
extension JournalDetailCoordinator: LoadImagePickerManager {
func loadImagePickerManager<T>(vc: T) where T : UIViewController amp; ImageGetterDelegate {
imagePickerManager = ImagePickerManager()
imagePickerManager.delegate = vc
imagePickerManager.pickImage(viewController: collectionViewController)
}
}
ViewController:
class JournalDetailViewController: UIViewController {
lazy var mainView: JournalDetailViewP = {
let view = JournalDetailViewP()
return view
}()
typealias myType = SetJournal amp; HasImagePickerManager
// dependency
var dep: myType!
var entryJournal: Journaling!
var tableViewDataSource: JournalDetailTVDataSource?
var collectionViewInteraction: AddImageCollectionViewInteraction?
weak var delegateLoadImagePickerManager: LoadImagePickerManager?
override func viewDidLoad() {
super.viewDidLoad()
title = "Detail Journal"
// only for testing without coordinator connection
// if entryJournal == nil {
// entryJournal = NewJournal()
// }
// dep = AppDependency()
setMainView()
loadTableView()
loadCollectionView()
}
override func viewDidDisappear(_ animated: Bool) {
print("view did disappear Journal Detail")
}
deinit {
dep.setJournal(newJournal: entryJournal)
print("JournalDetailViewController deinitialisiert")
}
@objc func getImage() {
delegateLoadImagePickerManager?.loadImagePickerManager(vc: self)
// dep.imagePickerManager.delegate = self
// dep.imagePickerManager.pickImage(viewController: self)
}
func saveEntry() {
}
}
extension JournalDetailViewController: Storyboarded {}
extension JournalDetailViewController: DependencyInjectionVC {}
extension JournalDetailViewController: SetMainView {}
extension JournalDetailViewController: ImageGetterDelegate {
func returnImage(image: UIImage) {
if entryJournal.image[0] == nil {
entryJournal.image[0] = image
} else {
entryJournal.image.append(image)
}
loadCollectionView()
}
}
extension JournalDetailViewController: AddImageCollectionViewInteractionDelegate {
func deleteImage(index: Int) {
}
func addImage() {
getImage()
}
}
Они освобождаются, если я не выполняю функцию getImage(), поэтому я думаю, что это причина круга хранения.
Это ImagePickerManager:
protocol ImageGetterDelegate: class {
func returnImage(image: UIImage)
}
class ImagePickerManager: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var imagePicker = UIImagePickerController()
weak var delegate: ImageGetterDelegate?
override init() {
super.init()
print("ImagePickerManager initialisiert")
}
deinit {
print("imagePickerManager deinitialisiert")
}
/// use to pick the Image, make sure to use the root ViewController to pass in to
func pickImage<T:UIViewController>(viewController: T) {
let alertList = UIAlertController(title: NSLocalizedString("Load Picture", comment: "Picture alert Alertcontroller"), message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) {
UIAlertAction in self.openCamera(viewController: viewController)
alertList.dismiss(animated: true, completion: nil)
}
let galleryAction = UIAlertAction(title: "Gallery", style: .default) {
UIAlertAction in self.openGallery(viewController: viewController)
alertList.dismiss(animated: true, completion: nil)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
alertList.dismiss(animated: true, completion: nil)
}
alertList.addAction(cameraAction)
alertList.addAction(galleryAction)
alertList.addAction(cancelAction)
viewController.present(alertList, animated: true, completion: nil)
}
private func openCamera<T:UIViewController>(viewController: T) {
if(UIImagePickerController .isSourceTypeAvailable(.camera)) {
imagePicker.sourceType = .camera
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
} else {
let warningAlert = UIAlertController(title: "Warning", message: "You do not have a camera", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Okay", style: .cancel) {
UIAlertAction in
warningAlert.dismiss(animated: true, completion: nil)
}
warningAlert.addAction(cancelAction)
viewController.present(warningAlert, animated: true, completion: nil)
}
}
private func openGallery<T:UIViewController>(viewController: T) {
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
}
@objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[.originalImage] as? UIImage else {
print("Expected a dictionary containing an image, but was provided the following: (info)")
return
}
delegate?.returnImage(image: image)
}
}
ImagePickerManager не выделяет после освобождения координатора. Итак, я думаю, что круг хранения связан с тем, что я передаю ViewVontroller обратно координатору в LoadImagePickerManager, а затем устанавливаю vc для координатора? У кого-нибудь есть идея, как решить эту проблему или что делать?
Редактировать: LoadImagePickerManager:
protocol LoadImagePickerManager: class {
func loadImagePickerManager<T: UIViewController amp; ImageGetterDelegate>(vc: T)
}
Я думаю, что утечка памяти происходит здесь при передаче collectionViewController:
imagePickerManager.pickImage(viewController: collectionViewController)
Поскольку я провел несколько тестов, если я не выполняю эту часть, то все освобождается нормально.
Обновленный класс ImagePickerManager:
class ImagePickerManager: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var imagePicker = UIImagePickerController()
weak var delegate: ImageGetterDelegate?
var viewController: UIViewController!
override init() {
super.init()
print("ImagePickerManager initialisiert")
}
deinit {
print("imagePickerManager deinitialisiert")
}
/// use to pick the Image, make sure to use the root ViewController to pass in to
func pickImage<T:UIViewController>(viewController: T) {
self.viewController = viewController
let alertList = UIAlertController(title: NSLocalizedString("Load Picture", comment: "Picture alert Alertcontroller"), message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) {
UIAlertAction in self.openCamera()
alertList.dismiss(animated: true, completion: nil)
}
let galleryAction = UIAlertAction(title: "Gallery", style: .default) {
UIAlertAction in self.openGallery()
alertList.dismiss(animated: true, completion: nil)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
alertList.dismiss(animated: true, completion: nil)
}
alertList.addAction(cameraAction)
alertList.addAction(galleryAction)
alertList.addAction(cancelAction)
viewController.present(alertList, animated: true, completion: nil)
}
private func openCamera() {
if(UIImagePickerController .isSourceTypeAvailable(.camera)) {
imagePicker.sourceType = .camera
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
} else {
let warningAlert = UIAlertController(title: "Warning", message: "You do not have a camera", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Okay", style: .cancel) {
UIAlertAction in
warningAlert.dismiss(animated: true, completion: nil)
}
warningAlert.addAction(cancelAction)
viewController.present(warningAlert, animated: true, completion: nil)
}
}
private func openGallery() {
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
viewController.present(imagePicker, animated: true, completion: nil)
}
@objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
guard let image = info[.originalImage] as? UIImage else {
print("Expected a dictionary containing an image, but was provided the following: (info)")
return
}
viewController = nil
delegate?.returnImage(image: image)
}
}
Я добавил переменную ViewController в класс и установил ее с помощью pickImage(), а затем, когда изображение выбрано, я установил переменную равной нулю. Затем UIViewController освобождается, но класс ImagePickerManager по-прежнему остается активным и не выделяется.
Комментарии:
1. Вам следует пересмотреть свой вопрос, чтобы публиковать как можно меньше кода. Прямо сейчас это похоже на стену кода.
2. Спасибо, хорошо, я обновлю вопрос. Проблема в том, что я не знаю точно, в чем проблема..
3. Я думаю, вы можете сократить свой пример кода с помощью минималистичного примера, который воспроизводит проблему и также является отражением вашего кода. Таким образом, больше людей смогут понять вашу проблему и помочь вам. 🙂
4. Проверка
AddImageCollectionViewInteractionDelegate
слаба или нет в его объекте источника данных.5. да, все делегаты слабые
Ответ №1:
Поскольку вы используете weak delegate so, это никоим образом не приведет к созданию цикла сохранения.
Я думаю, что ваш ViewController не освобождает выделение, потому что ваш ViewController все еще находится в стеке вашей навигации.
Попробуйте удалить все ViewControllers из стека навигации, и тогда ваш блок освобождения будет работать как обычно.
Попробуйте следующий код в зависимости от вашего требования (присутствует / нажимается), когда вы возвращаетесь к своему HomeViewController:
self.navigationController?.popToRootViewController(animated: true)
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
Редактировать:
Убедитесь, что ваш протокол относится к классовому типу, тогда будет работать только слабая ссылка.
protocol LoadImagePickerManager: class {
}
В вашем PickerManager попробуйте отклонить, используя следующий код, он перенаправит вас на контроллер rootview, но вы можете снова нажать или представить требуемому viewcontroller:
self.view.window?.rootViewController?.dismiss(animated: false, completion: nil)
Комментарии:
1. спасибо за ваш ответ. Но почему ViewController освобождает выделение, если getImage() не выполняется?
2. Я обновил свой код, да, мой протокол относится к классовому типу. Я также провел еще несколько тестов и обнаружил, что, по моему мнению, утечка памяти происходит при передаче CollectionViewController в ImagePickerManager и использовании его для AlertController.
3. итак, я думаю, что проблема в ImagePickerManager, я добавил переменную ViewController в ImagePickerManager и установил ее равной нулю после выбора изображения, после чего ViewControllers освобождаются. Но сам ImagePickerManager по-прежнему не освобождается, поэтому в этом классе что-то не так, я опубликовал обновленный класс ImagePickerManager. Спасибо за вашу помощь