Pull to refresh

Comments 15

К сожалению, не понял из статьи, зачем в рассматриваемом кейсе именно неуправляемый массив.
Если имеется ввиду чтение GIF хедера то непосредственно «неуправляемого» массива там нет, есть набор байт который управляется объектом типа NSData, я же описал как с помощью указателей интерпретировать данные из этого массива (в данном случае как строку и структуру с двумя целыми числами)
Один из подводных камней, видимо, выравнивание полей в структурах.
Верно, как следствие от выравнивания наличие минимального расстояния в массиве структур между структурами которое в общем случаем может не совпадать с размером структуры.
Мне так показалось, что это всё скорее нужно для сопряжения с кодом на Си, для написания над ним безопасных оберток. А ещё мне кажется, что лучше бы разрешали пользоваться такими штуками внутри блока unsafe, а то сейчас по коду:
let pSize = UnsafeMutablePointer<GifSize>(p)
// пусть тут будет 10000 строчек ещё
NSLog("Gif width = \(pSize.memory.width), height = \(pSize.memory.height)")

Становится неясно безопасно у нас всё или нет (мало ли кто решил обозвать свойство именем memory). Итого этот UnsafeMutablePointer может радостно по всему коду расползтись.
Да, вы правы если pSize будет иметь время жизни большее чем data то это приведет к чтению освобожденной памяти. Так что применяя указатели нужно быть очень внимательным.
Поэтому в Rust указатели могут быть использованы только внутри unsafe блока, а если они там были объявлены, то они его и покинуть не смогут. То есть ногу прострелить становится сложнее.
> в Rust указатели могут быть использованы только внутри unsafe блока

Смотря как понимать «использованы» — сырые указатели можно брать и передавать где угодно, вот разыменовать можно только в `unsafe{}`.

> а если они там были объявлены, то они его и покинуть не смогут

Не, сырые указатели можно куда угодно передавать же, в том числе и из блока:

https://play.rust-lang.org/?gist=17f0da2b3d478e51e0b7&version=stable
В Java для ускорения критически важных участков кода используют. Быть может, и тут получится.
Могли бы вы пояснить на каком-то другом примере преимущества от подобных операций — на чем реально можно много сэкономить ресурсов.
Просто в данном случае размер картинки, конечно, удалось получить без каких-либо сторонних библиотек, но для другого программиста данный код в проекте может вызвать больше вопросов. Тут выгода не так очевидна, на мой взгляд.
let p = UnsafeMutablePointer<Int>.alloc(1)
p.memory = 20
p.dealloc(1)


Во второй строчке следует использовать метод initialize(), так как память ещё не инициализирована. Только после этого можно работать с memory. До того, как освобождать память, следует вызвать метод destroy().

Память, на которую указывает Unsafe(Mutable)Pointer, может быть инициализирована или нет. Очень важно использовать правильные методы для памяти в различных состояниях. Так как ваш указатель указывает на Int, то скорее всего в этом конкретном случае ничего плохого не случится, но как только вы начнёте работать с типами, для которых копия экземпляра не создаётся тривиальным побитовым копированием, не использование правильных методов почти наверняка приведёт к падениям программы и утечкам памяти. Посмотрите документацию на UnsafeMutablePointer, там довольно подробно написано про состояния, в которых он может находиться.

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


Чаще всего прямая работа с указателями нужна для вызова API на C, которые принимают сложные структуры данных с указателями внутри.
initialize() и destroy() предназначены для работы с объектами, для основных типов они ничего не делают. Впрочем вы правы, стоит поправить, чтобы не возникало недопонимания.
Существует контракт API, который описан в документации. Если его не выполнять, а просто написать код и заметить что и так, кажется, работает, то это просто везение. Кроме того, в будущем может сломаться.
В общем случае абсолютно согласен!
Sign up to leave a comment.

Articles