Почему инициализация этого быстрого объекта производит ARC 2 вместо 1? - PullRequest
0 голосов
/ 02 октября 2018

Я столкнулся с проблемой в своем проекте и понял, что объект не освобождается должным образом.Я решил проверить ARC объекта, и сразу после инициализации он равен 2. В этом тривиальном примере ниже то же самое верно.Почему ARC 2, а не 1?

import SpriteKit

class LevelBuilder:SKNode{
    var testNode:SKSpriteNode?
    init(with color:SKColor){
        super.init()
        self.testNode = SKSpriteNode(color: color, size: CGSize(width: 2, height: 2))
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

let test = LevelBuilder(with: .red)
print("ARC: \(CFGetRetainCount(test))")

Он печатает ARC: 2

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Не существует такой вещи, как «ДУГА объекта».То, о чем вы думаете, это удержание счета.Трудно представить число более бессмысленным, чем счет удержания.Это либо ноль (в этом случае объект пропал, так что вы никогда этого не увидите), либо «не ноль».

Счет удержания - это количество заявок на владение, которые были предъявленыобъект.Любая часть системы может в любое время заявить о праве собственности.Любая часть системы может отозвать свое право собственности в любое время.Существует целая вещь, называемая пулом авто-выпусков, в котором хранятся заявки на владение, и они будут автоматически аннулироваться "в какой-то момент в будущем".Для объекта совершенно нормально иметь несколько авто-выпусков, которые сохраняются на нем в любой момент времени.Это увеличит количество сохраняемых данных, но количество сохраняемых данных уменьшится позже.

Если количество сохраняемых данных было бессмысленным в MRC (и они были), они полностью помешали в ARC, где компилятор может оптимизировать ихв любое время он может доказать, что это не имеет значения, и часто вводит дополнительные удержания, когда не может доказать, что они не нужны (особенно связанные с вызовами функций).Таким образом, фактическая стоимость еще более бессмысленна.Например, в ARC для test вполне уместно добавить дополнительное удержание перед вызовом CFGetRetainCount, чтобы убедиться, что test не высвобождается слишком быстро.

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

В вашем конкретном случае мы можем исследовать это немного с помощью swiftc -emit-sil, начиная с того момента, когда мы выполняем интерполяцию строк (то есть "" в последнемстрока):

// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%34 = function_ref @$SSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %35
%35 = apply %34(%30, %31, %32, %33) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %37
%36 = alloc_stack $String                       // users: %39, %37, %41
store %35 to %36 : $*String                     // id: %37
// function_ref specialized String.init<A>(stringInterpolationSegment:)
%38 = function_ref @$SSS26stringInterpolationSegmentSSx_tcs23CustomStringConvertibleRzs20TextOutputStreamableRzlufCSS_Tg5 : $@convention(method) (@owned String, @thin String.Type) -> @owned String // user: %40
%39 = load %36 : $*String                       // user: %40
%40 = apply %38(%39, %29) : $@convention(method) (@owned String, @thin String.Type) -> @owned String // user: %42
dealloc_stack %36 : $*String                    // id: %41
store %40 to %28 : $*String                     // id: %42
%43 = integer_literal $Builtin.Word, 1          // user: %44
%44 = index_addr %28 : $*String, %43 : $Builtin.Word // user: %58
%45 = metatype $@thin String.Type               // user: %56
%46 = load %3 : $*LevelBuilder                  // users: %48, %47

=========
strong_retain %46 : $LevelBuilder               // id: %47
%48 = init_existential_ref %46 : $LevelBuilder : $LevelBuilder, $AnyObject // user: %49
%49 = enum $Optional<AnyObject>, #Optional.some!enumelt.1, %48 : $AnyObject // users: %52, %51
// function_ref CFGetRetainCount
%50 = function_ref @CFGetRetainCount : $@convention(c) (Optional<AnyObject>) -> Int // user: %51
%51 = apply %50(%49) : $@convention(c) (Optional<AnyObject>) -> Int // user: %54
release_value %49 : $Optional<AnyObject>        // id: %52
=========

Важную часть я пометил === строками.Сильное удержание накладывается на test.Затем он помещается в оболочку AnyObject? для передачи в функцию C (GetRetainCount).Функция называется.И тогда значение Optional (то есть test) освобождается.Поэтому вы должны ожидать одно дополнительное сохранение при вызове GetRetainCount.

Но если вы перекомпилируете это с -O, вы заметите, что инструкции strong_retain нет.ARC видит, что дополнительное удержание на самом деле не нужно, и удаляет его.Таким образом, это говорит о том, что с оптимизацией количество сохранений будет равно 1. Интересно, правда ли это:

$ swiftc main.swift
$ ./main
ARC: 2
$ swiftc -O main.swift
$ ./main
ARC: 1

Конечно.

0 голосов
/ 02 октября 2018

Может быть, потому что вы инициализируете свой testNode, который является SKSpriteNode, который также может ссылаться на SKNode под капотом.Итак, у вас есть первая ссылка из вашего класса LevelBuilder, а вторая - из testNode.

...