Одним из недостатков языка Swift является его бесконечно изменяющийся синтаксис (и ненадежность преобразователя синтаксиса). Это и страх разработчика языка перед указателями.
Я работал с большими массивами аудиоданных. Естественно, я также использую довольно приятную структуру Apple Accelerate для таких операций, как БПФ, оконные функции и тому подобное.
Если у меня есть массив примерно так:
var veryBigArray = Float [повторение: 0,0, количество: 1024 * 1024]
и я хочу выполнить оконную операцию + БПФ на 1024 элементах, начиная с элемента n, это оказалось невероятно сложно сделать. Ссылка на n-й элемент заставляет компилятор копировать n-й элемент в другое место, а затем давать мне ссылку на новое временное местоположение. Для меня это, конечно, совершенно бесполезно. Предположительно, это связано с тем, что ссылка может привести к изменению содержимого элемента и, таким образом, вызвать копирование при записи (я думаю, что это заставляет страх перед изменчивостью выходить за рамки разумного - классы уже имеют такую изменчивость, и небо не упало).
Я мог бы использовать:
Массив (veryBigArray [n .. ‹n + 1024])
но это приводит к ужасной операции копирования, а если она выполняется в цикле, быстро ставит машину на колени. Я мог бы передать фрагмент, но я понятия не имею, каковы могут быть правила для любых недокументированных операций копирования, которые могут иметь место.
Я действительно начал переносить это на C, когда подумал, что, возможно, я мог бы использовать что-то вроде этого:
var basePtr = UnsafeMutableBufferPointer(start: &veryBigArray, count: veryBigArray.count)
let ptr = UnsafePointer<Float>(basePtr.baseAddress)
var ptr1 = ptr! + n
Теперь я могу вызывать функции ускорения. Например:
vDSP_sve(ptr1, 1, &aResultFloat, vDSP_Length(1024))
Это действительно работает и может быть даже моим лучшим вариантом (учитывая, что у меня огромные инвестиции в Swift, хотя использование C было бы намного более рациональным вариантом). Обратной стороной является в основном то, что это неуклюже, а указатели не принадлежат, что исключает использование ARC.
Немного расширившись, я просто читаю аудиобуфер из ресурса. Я хотел бы иметь собственный указатель на буфер, который я могу обнулить, когда я хочу, чтобы ARC избавился от буфера.
Вот как я читаю ресурс:
func loadAudioFile(fileName: String) -> (UnsafePointer<Float>?, Int) {
// let audioSession = AVAudioSession.sharedInstance()
let fileURL = Bundle.main.url(forResource: fileName, withExtension: "aiff")
if fileURL == nil {return (nil, 0) }
if let audioFile = try? AVAudioFile(forReading: fileURL!) {
let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: UInt32(audioFile.length))
if ((try? audioFile.read(into: buffer)) != nil) {
return (UnsafePointer(buffer.floatChannelData?.pointee), Int(audioFile.length))
}
}
return (nil, 0)
}
Но мне интересно, а может быть вариант получше?
ContiguousArray в принципе привлекателен, но по сути недокументирован и не особенно совместим с обычными массивами. Он также страдает от того, что кажется невероятно глупым решением не разрешать адресные ссылки на элементы массива (они обрабатываются так же глупо, как и с обычными массивами).
Я мог передавать UnsafeMutableBufferPointers взад и вперед по моей системе. Если я дам этой скороговорке типалиас (который с тех пор был переименован в связанный тип, пусть кто-нибудь на самом деле догадывается, для чего он нужен), это может быть не совсем ужасно ... и у него есть преимущество в том, что он гарантирует непрерывность. Но это кажется даже более громоздким, чем переход к obj-c для выполнения тяжелой работы.
Я должен признаться, что разочарован тем, что мне кажется очень простой и очень распространенной операцией, которую практически невозможно реализовать в Swift.
Лучшее, что я видел до сих пор, было опубликовано Крисом Лиссио пару лет назад. Конечно, с тех пор, как он опубликовал его, синтаксис изменился, и преобразователь синтаксиса не работает с его кодом, но я, безусловно, мог бы использовать его как отправную точку и создать что-то, что удовлетворяет мои потребности.
Я был очень взволнован, когда объявили о Swift. Язык казался хорошо спроектированным и даже скомпилированным в нативный код, что было для меня важно. Предполагалось, что он будет совместим с C. Так что я взял на себя серьезное обязательство.
Но последние два года были отмечены бесконечным разочарованием из-за нестабильных компиляторов, синтаксиса, который, казалось бы, меняется с каждой бета-версией, и очевидной враждебностью со стороны основной команды Swift по отношению к людям, которые действительно хотят использовать свой игрушечный язык для реальной работы ( поэтому я не вижу смысла поднимать предложение по языку, которое гарантированно ни к чему не приведет).
Есть ли у кого-нибудь лучший способ подойти к проблеме передачи адреса места, находящегося внутри большого аудиобуфера, функциям Accelerate? С каждым годом я замечаю, что появляется все больше умных людей, которые придумали более эффективные способы решения задач, чем я, поэтому я хотел бы услышать мнение любого, у кого есть достойное решение. Или даже идея, которая может указать на хорошее решение.