1

Примеры сильных ссылочных циклов, которые я обычно вижу, включают два класса со свойствами, указывающими друг на друга. Однако, что если только один из классов имеет свойство, указывающее на другой, например, как это:Является ли это сильным эталонным циклом или утечкой памяти вообще?

class ClassA { 
    var classB: ClassB? = nil 
} 

class ClassB { 

} 

Затем я создаю свои экземпляры, как это:

var myClassA = ClassA() 
var myClassB = ClassB() //Reference count 1 
myClassA.classB = myClassB //Reference count 2 

// Now deallocate 
myClassB = nil //Reference count 1 
myClassA = nil 

Поскольку я освобождаться myClassB, то счетчик ссылок равен 1. Что случилось с подсчетом ссылок myClassA.classB? Это никогда не достигало нуля, так как я никогда не делал myClassA.classB = nil или использовал deinit для этого. Это неявно сделано, так как я сделал myClassA = nil?

Это то, что может быть отнесено к категории сильного справочного цикла? Я бы предположил, что это хотя бы утечка памяти, правда, это правда?

+2

В коде отсутствует ни утечка, ни круговая ссылка. Когда вы устанавливаете 'myClassA' на' nil', ARC автоматически нарушает право собственности на 'ClassB', поэтому оба они освобождаются. – ozgur

+0

Чтобы расширить это, нет цикла, так как ваш график имеет только один край: 'myClassA.classB -> myClassB'. – jtbandes

+0

Если этот код вызвал ссылку, это сделало бы ARC только незначительно полезным. Все ссылки, хранящиеся на уровне экземпляра, освобождаются при выпуске экземпляра. – Avi

ответ

2

Как @ozgur, @jtbandes, @Avi и @Rob объясняются в комментариях, нет сильного эталонного цикла или утечки.

Вот пример, основанный на @ комментарий Роба, что вы можете работать в Playground:

class ClassA { 
    var classB: ClassB? 

    deinit { 
     print("ClassA deallocated") 
    } 
} 

class ClassB { 
    deinit { 
     print("ClassB deallocated") 
    } 
} 

class Tester { 
    func test() { 
     var myClassA: ClassA! = ClassA() 
     var myClassB: ClassB! = ClassB() //Reference count 1 
     myClassA.classB = myClassB //Reference count 2 

     // Now deallocate 
     print("setting myClassB to nil") 
     myClassB = nil //Reference count 1 
     print("setting myClassA to nil") 
     myClassA = nil 
     print("all done") 
    } 
} 

// Create `Tester` object and call `test`: 

Tester().test() 

Выход:

setting myClassB to nil 
setting myClassA to nil 
ClassA deallocated 
ClassB deallocated 
all done 

Дело отметить, что даже если вы set myClassB до nil сначала, что myClassA сначала освобождается. Когда myClassA освобождается, окончательная ссылка на myClassB выдается ARC, а затем myClassB освобождается.


Чтобы продемонстрировать сильный опорный цикл, у ClassB сохраняют сильную ссылку на ClassA:

class ClassA { 
    var classB: ClassB? 

    deinit { 
     print("ClassA deallocated") 
    } 
} 

class ClassB { 
    var classA: ClassA? 

    deinit { 
     print("ClassB deallocated") 
    } 
} 

class Tester { 
    func test() { 
     var myClassA:ClassA! = ClassA() 
     var myClassB:ClassB! = ClassB() //Reference count 1 
     myClassA.classB = myClassB //Reference count 2 
     myClassB.classA = myClassA 

     // Now deallocate 
     print("setting myClassB to nil") 
     myClassB = nil //Reference count 1 
     print("setting myClassA to nil") 
     myClassA = nil 
     print("all done") 
    } 
} 

Tester().test() 

Выход:

setting myClassB to nil 
setting myClassA to nil 
all done 

Ни объект освобождаться, если они оба содержат сильную ссылку на другую. Чтобы разбить этот сильный опорный цикл, объявите один из свойств classB или classA значением weak.Какой из них вы выбираете влияет на порядок, в котором объекты получают освобожденный:

Если вы объявляете weak var classB: ClassB в ClassA:

Выход:

setting myClassB to nil 
ClassB deallocated 
setting myClassA to nil 
ClassA deallocated 
all done 

Если вместо этого, вы объявляете weak var classA: ClassA in ClassB:

Выход:

setting myClassB to nil 
setting myClassA to nil 
ClassA deallocated 
ClassB deallocated 
all done 
Смежные вопросы