Самоизменяющаяся структура Swift в фоновом потоке

Предположим, у нас есть структура, способная к самомутации, которая должна происходить как часть фоновой операции:

struct Thing {
    var something = 0
    mutating func operation(block: () -> Void) {            

        // Start some background operation
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {

            // Mutate self upon background task completion
            self.something += 1
            block()

        }

    }
}

Теперь, когда я использую такую ​​структуру в контексте:

var myThing = Thing()
myThing.operation {
    println(myThing.something)
}

println дает мне 0, как будто myThing никогда не мутировал. Печать self.something из dispatch_async, очевидно, дает 1.

Как мне обойти эту проблему, предпочтительно без необходимости передавать обновленную структуру self в соревновательный блок operation и переопределять исходную переменную в основном контексте?

// Ew
var myThing = Thing()
myThing.operation {
    (mutatedThing) in
    myThing = mutatedThing
    println(myThing.something)
}

person Arnold    schedule 07.09.2015    source источник


Ответы (2)


Я добавляю второй ответ, потому что мой первый ответ касался другого вопроса.

Я сам только что столкнулся с этой трудностью в обстоятельствах, почти идентичных вашим.

После работы, работы и работы, пытаясь выяснить, что происходит, и исправить это, я понял, что проблема была, в основном, в использовании типа значения вместо ссылочного типа.

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

С другой стороны, желаемое поведение состоит в том, чтобы действия, выполняемые в замыкании, сохранялись средой за пределами замыкания — другими словами, два разных контекста (внутри замыкания и вне его) должны ссылаться на одни и те же объекты — -что больше соответствует поведению ссылочного типа.

Короче говоря, я изменил структуру на класс. Проблема исчезла, никакого другого кода не понадобилось.

person Le Mot Juiced    schedule 15.10.2015
comment
Да, я также пришел к выводу, что я просто совершенно неправильно использую структуры и делаю странные неправильные вещи. Спасибо за подробное объяснение этого в ответе. Для потомков: faq.sealedabstract.com/structs_or_classes - person Arnold; 19.10.2015
comment
Это большая проблема со структурами. Блоки сохраняют значения. Для объекта ref он допускает мутацию, для структуры он будет копировать и «мутировать» только эту копию. Я думал об использовании inout для блока, но: title="параметр быстрого захвата inout в замыканиях, которые избегают вызываемой функции"> stackoverflow.com/questions/30020250/ - person bauerMusic; 11.04.2018

Я видел именно эту проблему много раз, но без подробностей я не могу сказать, происходит ли это по той же причине, что и у вас.

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

В моем случае проблема исчезла, как только я правильно запланировал отправку. Надеюсь, это поможет!

person Le Mot Juiced    schedule 22.09.2015
comment
Спасибо, это определенно отличная зацепка. Что именно вы подразумеваете под правильным планированием отправки? Вы установили семафор, чтобы получать уведомления об окончании? - person Arnold; 23.09.2015
comment
В моем случае правильное планирование просто означает планирование событий в будущем, а не в прошлом. Я выполнял большую часть своей работы в Playgrounds и пытался координировать различные движущиеся части, которые передавали друг другу значения dispatch_time_t. После долгих размышлений я понял, что ошибочное закрытие было запланировано для срабатывания с использованием dispatch_time_t, которое к тому времени, когда команда dispatch_after получила его, уже было в прошлом. - person Le Mot Juiced; 23.09.2015