2015-05-03 2 views
4

Я попытался написать класс «редактор», который мог бы сохранить ссылку на свойство для другого объекта для последующей мутации. Я сначала написал класс редактора, чтобы получить закрытие для чтения, и закрытие для записи. Это сработало. Затем я попытался передать рассматриваемый параметр ссылкой (inout), а затем сгенерировал пару геттер/сеттер. Это не сработало. Документы Swift говорят (перефразируя), что Свифт указывает, когда копировать, а когда нет. Я думаю, что я против непредсказуемости этого ограничения, но думал, что я поставил бы вопрос точно так же.Swift: Захват inout параметра в замыканиях, которые вызывают вызванную функцию

В качестве альтернативы можно ли получить функцию карри для индивидуального геттера и сеттера?

Мой код:

class SomeModel : Printable { 

    var a:String 

    init(a:String) { 
     self.a = a 
    } 

    var description:String { 
     return "\(self.a)" 
    } 
} 


class Editor { 

    var getter:()-> String 
    var setter:(String)->() 

    init(getter:()-> String, setter:(String)->()) { 
     self.getter = getter 
     self.setter = setter 
    } 

    convenience init(inout bindTo:String) { 
     self.init(
      getter:{ return bindTo }, 
      setter: { v in bindTo = v }) 
    } 

    func read() -> String { 
     return self.getter() 
    } 

    func write(value:String) { 
     self.setter(value) 
    } 
} 


func testBindTo() { 
    var readModel = SomeModel(a:"Did not capture by reference"); 
    var bindForReading = Editor(bindTo: &readModel.a) 
    readModel.a = "captured by reference!" 
    println(bindForReading.read()) 

    var writeModel = SomeModel(a:"Did not capture by reference"); 
    var bindForWriting = Editor(bindTo: &writeModel.a) 
    bindForWriting.write("captured by reference") 
    println(writeModel) 
} 

testBindTo() 


func testExplicitGetterSetter() { 

    var readModel = SomeModel(a:"Did not capture by reference"); 
    var bindForReading = Editor(
     getter: { readModel.a }, 
     setter: { v in readModel.a = v }) 
    readModel.a = "captured by reference!" 
    println(bindForReading.read()) 

    var writeModel = SomeModel(a:"Did not capture by reference"); 
    var bindForWriting = Editor(
     getter: { writeModel.a }, 
     setter: { v in writeModel.a = v }) 
    bindForWriting.write("captured by reference") 
    println(writeModel)  
} 

testExplicitGetterSetter() 

Результаты:

Did not capture by reference 
Did not capture by reference 
captured by reference! 
captured by reference 

Спасибо!

ответ

5

Я не думаю, что это возможно. И не должен быть в состоянии, если вы думаете об этом, потому что это было бы супер небезопасно.

Поскольку замыкания могут пережить область действия, в которой они были созданы, захваченные переменные должны храниться вместе с блоком. Но чтобы иметь возможность назначать захваченную переменную и делиться состоянием этой переменной между (одним или несколькими) блоком (-ами), которые его захватили, и исходной областью, блоки не могут просто захватить значение переменной (которая создавал бы независимые копии переменной), но фиксирует своего рода «ссылку» на общую копию. Это означает, что назначаемые переменные, которые захватываются блоками, должны храниться специально. В Objective-C это объявлено с помощью __block. В Swift это поведение __block неявно.

Однако для того, чтобы блок мог изменить переменную inout (возможно, в более позднее время), как это видно из области действия вызывающего объекта, это означает, что передаваемая переменная в области вызывающего абонента также должна быть сохранена таким образом, который может пережить фрейм стека. Но функция вызывающего абонента этого не знает. Все, что известно из типа вызываемой функции, состоит в том, что один из ее параметров - inout; он не знает, что функция планирует захватить эту переменную inout в блоке. Поэтому он не знает, чтобы подготовить это хранилище __block для этой переданной переменной.

+0

Возможно, мне что-то не хватает, кажется, что есть две проблемы: семантика семантики закрытия и время жизни объекта. К последнему моменту вы уже можете создать класс (ссылочный тип) в стеке и вызвать функцию, которая хранит ее вне времени жизни функции. В первую очередь, однако, автоматическая копия семантики Swift, похоже, заботится о проблеме с блоком, они просто не играют вместе. То, что я действительно хочу, - это получить теневой геттер/сеттер. Надеюсь, в какой-то момент они добавят синтаксис. Спасибо за ваш ответ. –

+0

@chrisco: речь идет не о «объектах» (на которые указывают переменные ссылочных типов), а сами * переменные * (либо переменные типа значения, либо ссылочный тип, это не имеет значения). A * локальная переменная *, независимо от Objective-C или Swift, имеет только время жизни области, в которой она была объявлена, если только она не объявлена ​​'__block' в Objective-C или не используется в закрытии в Swift, и в этом случае компилятор сохраняет его таким образом, что он может жить дольше. – newacct

+0

@newacct ваше объяснение, похоже, имеет отверстие в нем, потому что __block требуется только для значений, которые будут мутированы. Другие значения захватываются без специальной аннотации и вне зависимости от их кадров стека без проблем. В общем, то, что вы сказали, похоже, имеет большой смысл. Моя проблема в том, что эта функциональность необходима. Для правильного хранения этих значений должно быть ключевое слово swift, чтобы они могли быть мутированы таким образом. – BTRUE

Смежные вопросы