Всегда ли нужен [Weak self] при работе с URLSession? - PullRequest
0 голосов
/ 25 ноября 2018

Я не могу понять, нужно ли мне использовать [weak self] в этой ситуации или нет?

HTTPClient.swift:

struct HTTPClient {
    let session = URLSession.shared

    func get(url: URL, completion: @escaping (Data) -> Void) {
        session.dataTask(with: url) { data, urlResponse, error in
        completion(data) // assume everything will go well
      }.resume()
    }
}

Service.swift

struct Service {
    let httpClient: HTTPClient

    init(httpClient: HTTPClient = HTTPClient()) {
        self.httpClient = httpClient
    }

    func fetchUser(completion: @escaping (User) -> Void) {
        httpClient.get("urlToGetUser") { data in
          // transform data to User 
          completion(user)
        } 
    }
}

ViewModel.swift

class ViewModel {
   let service: Service
   let user: User?
   var didLoadData: ((User) -> Void)?

    init(service: Service) {
        self.service = service
        loadUser()
    }

   func loadUser() {
     service.fetchUser { [weak self] user in // is  [weak self] really needed ?
            self?.user = user
            self?.didLoadData?(user)
        }
   }
}

это действительно нужно здесь для пользователя[weak self]?и есть ли правило о том, как проверить, нужно ли это вообще, когда мы имеем дело с API, что мы не знаем, что происходит с закрытием?или это не имеет значения (решать нам)?

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

В приведенном вами примере [weak self] потенциально не требуется.Это зависит от того, что вы хотите, если ViewModel будет выпущено до завершения запроса.

Как отмечено в URLSessionDataTask документах (выделено мной):

После созданиязадача, вы запускаете ее, вызывая ее метод resume ().Затем сеанс поддерживает строгую ссылку на задачу до тех пор, пока запрос не завершится или не завершится с ошибкой ;вам не нужно поддерживать ссылку на задачу, если только она не полезна для внутренней бухгалтерии вашего приложения.

Сессия имеет четкую ссылку на задачу.Задача имеет сильную ссылку на закрытие.Закрытие имеет сильную ссылку на ViewModel.Пока ViewModel не имеет четкой ссылки на задачу (чего нет в коде, который вы предоставили), цикл отсутствует.

Вопрос в том, хотите ли вы убедиться, чтоViewModel продолжает существовать достаточно долго для выполнения замыкания.Если вы делаете (или не заботитесь), то вы можете использовать простую сильную ссылку.Если вы хотите помешать задаче сохранить ViewModel в действии, вам следует использовать слабую ссылку.

Именно так вы должны думать о ссылочных циклах.Не существует общего правила «используйте weak здесь».Вы используете weak, когда вы это имеете в виду;когда вы не хотите, чтобы это закрытие сохраняло self, пока оно не будет выпущено.Это особенно верно, если это создает цикл.Но нет общего ответа на вопрос «это создает цикл».Это зависит от того, какие части содержат ссылки.

Это также указывает на то, где ваш текущий дизайн API не так хорош, как мог бы быть.Вы проходите didLoadData в init.Скорее всего, это создаст эталонные циклы и заставит вашего абонента использовать weak.Если вместо этого вы сделали didLoadDdata обработчиком завершения на loadUser(), то вы могли бы избежать этой проблемы и упростить жизнь вызывающей стороне.

func loadUser(completion: @escaping ((User?) -> Void)? = nil) {
    service.fetchUser {
        self?.user = user
        didLoadData?(user)
    }
}

(Ваш текущий API имеет условие гонки в любом случае.Вы запускаете loadUser() до того, как можно установить didLoadData. Возможно, вы предполагаете, что обработчик завершения не завершит работу до того, как вы установили dataDidLoad, но на самом деле это не обещает. Возможно, это правда, но этов лучшем случае хрупкий.)

0 голосов
/ 26 ноября 2018

Проблема с вашим кодом связана не с использованием URLSession, а с тем фактом, что вы сохраняете функцию в контроллере представления:

class ViewModel {
    var didLoadData: ((User) -> Void)?
}

Если функция didLoadData упоминает self (т.е. экземпляр ViewModel) неявно или явно, у вас есть цикл сохранения и утечка памяти, если вы не скажете weak self или unowned self.

...