Как стать автором
Обновить

Комментарии 17

Большое спасибо за статью! А каков размер SideTable? Указатель + 3 числа. 32 байта (на 64-битной системе)?
Правильно ли я понимаю, что слабые ссылки зануляются только при обращении к ним (они обращаются к SideTable, видят, что объект удалён, и зануляются, счётчик слабых ссылок уменьшается)? Если да, то я правильно понимаю, что, если в цикле создать 10^6 объектов + по 1 слабой ссылке на объект, а потом удалить все объекты, то у нас, если мы не будем трогать слабые ссылки, останется висеть в памяти 32 мегабайта SideTables?

Спасибо за хороший вопрос! Насчет размера — я затрудняюсь сразу ответить, но могу предположить, что меньше, использую int32 для счетчиков. Сама SideTable представляет из себя такое хранилище, то есть может иметь еще дополнительные флаги:


HeapObjectSideTableEntry {
  SideTableRefCounts {
    object pointer
    atomic<SideTableRefCountBits> {
      strong RC + unowned RC + weak RC + flags
    }
  }   
}

Саму реализацю SideTable можно посмотреть тут
Я попробую покопать и узнать примерные размеры и вернусь к вам с ответом)


Второй вопрос, про то, когда удаляются боковые таблицы — да, проверка осуществляется при обращении по ссылке, поэтому важно, чтобы SideTable была максимально маленькой.

Спасибо за статью! Очень познавательно для новичков
Большое спасибо за статью. Многие моменты прояснились после прочтения.
Глаз зацепился за фразу:
На объект также может быть unowned ссылка, которая прибавляет +1 к unowned и +1 к strong.

Правильно ли я понял что unowned ссылка увеличивает и unowned счетчик и strong счетчик?

Да, все так
Об этом еще описано в блоке Инварианты счетчиков ссылок

То есть deinit не вызовется пока не пропадут все unowned ссылки?
Аааа походу понял. Не каждая unowned ссылка прибавляет +1, а просто к счетчику strong ссылок прибавляется +1, а потом после того как deinit отработал у счетчика делается -1.
Можно еще попросить у вас пару слов о том как устроены коллекции и тд?
Например struct Test -> array: [Bool] = 8 байт. Почему так и что будет если он меняется.

Тема коллекций на самом деле достаточная большая, но если кратко, то Array в Swift представляет из себя некую обертку над данными, и позволяет соблюсти value семантику языка (данные неизменяемые, копируемые и тп). Это структура, у которой всегда известен заранее размер для MemoryLayout. А сами данные уже размещаются динамично в других участках памяти, на которые Array имеет ссылки. Именно поэтому MemoryLayout нам не отдает "настоящий" размер массива.


Также об этом сказано в документации метода size(ofValue:)


The result does not include any dynamically allocated or out of line storage. In particular, pointers and class instances all have the same contiguous memory footprint, regardless of the size of the referenced data.
Спасибо за статью! Есть пара вопросов.

1) Вы пишете:
счетчики инициализируются со значениями strong  —  1, unowned  —  0, weak  —  0

В статьях, которые прикладываете, по-другому:
Object's refcounts are initialized as 1 strong, 1 unowned, 1 weak. Кто прав?

2) Вы также пишете
На объект также может быть unowned ссылка, которая прибавляет +1 к unowned и +1 к strong.

и
Счетчик unowned ссылок добавляет +1 к strong, который впоследствие уменьшается после завершения deinit-a объекта.

Если счетчик unowned ссылок добавляет +1 к счетчику сильных ссылок, то счетчик сильных ссылок объекта, имеющего unowned ссылку, никогда не опустится ниже 1. Получается, что объект на который нет сильных ссылок, но есть unowned ссылка, никогда не deinit-нится.

Вообще судя по тому, что написано в хэдере RefCount.h:

The unowned RC also has an extra +1 on behalf of the strong references; this +1 is
decremented after deinit completes.
The weak RC also has an extra +1 on behalf of the unowned references; this +1 is decremented
after the object's allocation is freed.

Кажется это означает, что:
Unowned RC получает +1 пока у нас есть сильные ссылки и не вызван deinit;
Weak RC получает +1 пока у нас есть unowned references и объект не будет освобожден(freed);

Насколько я понимаю, эти +1 как раз и нужны, чтобы обеспечить корректную работу стэйт машины (потому это и инварианты собственно).

Поправьте меня если я не прав.
собственно поэтому у нас при создании объекта стартовые значения 1 strong, 1 unowned, 1 weak, хотя unowned и weak ссылок пока нет. У unowned +1 за счет 1 strong, у weak +1 за счет 1 unowned.
Вот вопрос про
1 weak
как у нас может быть 1 weak, если при создании у объекта нет side table, которая создастся после появления первой weak ссылки.
Или подразумевается, что это не физический, а логический 1 weak?
В RefCount.h
LIVE without side table
The object is alive.
Object's refcounts are initialized as 1 strong, 1 unowned, 1 weak.
No side table. No weak RC storage.

Как будто противоречие
1 weak
и
No weak RC storage

side tablе нет пока не добавится слабая ссылка(ну или выполнится одно из других описанных условий типа переполнения счетчика или наличия assosiated объектов), все верно:

Weak variable store adds the side table, becoming LIVE with side table.

Но значение weak каунтера 1 на старте.

Получается weak и unowned счетчики не совсем честные, а +1 для инвариантности

1) Ох, это моя ошибка/недоглядка, тут вы совершенно правы, спасибо, что подметили, я поправлю!
2) Да, еще раз спасибо за замечание, помогаете сделать статью еще лучше :)
Вы верно описали, я ошибся тут, что unowned прибавляет к strong, как раз такие наоборот, strong прибавляет к unowned, а unowned к weak. Также поправлю


И да, это все для корректной работы state машины только необходимо.
С меня плюсы причитаются :)

Спасибо за статью, теперь стало понятнее почему лучше использовать структуры чем объекты для хранения данных:)

Здравствуйте, спасибо за статью!

2. Это позволяет безопасно обнулять слабые ссылки, поскольку слабая ссылка теперь не указывает напрямую на объект и не является предметом race condition.

Слабая ссылка будет указывать на Side Table, но я не понимаю, почему она теперь не предмет race condition. Два потока все еще в теории могут одновременно обратиться к таблице, что вызовет уменьшение счетчика weak ссылок дважды, что приведет к той же проблеме, что вы описали в случае до Swift 4: какой-то механизм посчитает, что количество слабых ссылок равно нулю и сначала уничтожит таблицу, а затем попытается сделать это еще раз.

Получается, что у нового механизма есть какая-то система потокобезопасности? Почему ее просто не ввели для Swift<4?

Второй вопрос касаемо состояний объекта.
На одном из рисунков видим, что при появлении Side Table счетчики сильных и unowned ссылок туда переносятся. А вот на схеме состояний заметим, что объект будет в состоянии deinited всегда, пока у него будут unowned ссылки. Но ведь это не так! Если существует Side Table, то счетчик unowned ссылок находится там, а значит, что объект в этом случае попадет в состояние Freed?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий