I've recently started profiling one of my iOS app written in swift and realized how annoying ARC is compared to other more adopted GC like Mark-And-Sweep.
One of the most prevalent causes for Strong Reference Cycle was retaining instance property within a closure that is passed to another object retained, again, by the class.
For example,
class MyClass {
private let text = "hello world"
private let anotherClass = AnotherClass()
init() {
addText()
}
private func addText() {
anotherClass.addText { return self.text }
}
}
Retain cycle like above can be avoided by passing argument to the method instead of accessing self directly.
class MyClass {
private let text = "hello world"
private let anotherClass = AnotherClass()
init() {
addText(text)
}
private func addText(text:String) {
anotherClass.addText { return text }
}
}
Is the second approach considered a good practice?
FYI, I'm aware that retain cycle like above can be broken using capture list. I'm just curious as to patterns that are more resilient to memory leaks.
Since the property let text ... is a constant, I think that the second approach is fine and can be considered a best practice.
But if you use the same approach with a var text ... property, then the behaviour changes:
in the first approach an execution of the closure will use the current value of text,
while in the second approach an execution of the closure will always use the original value of text.
Related
I'm building a Kotlin library to use in my iOS app using Kotlin/Native. After I call some methods in the library from Swift, which works, I also want to call methods in Swift from the library. To accomplish this I implemented an interface in the library:
class Outbound {
interface HostInterfaceForTracking {
fun calcFeatureVector(bitmap: Any?): Array<Array<FloatArray>>?
}
var hostInterface: HostInterfaceForTracking? = null
fun registerInterface(hostInterface: HostInterfaceForTracking) {
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
}
}
This is implemented on the Swift side like this:
class HostInterfaceForTracking : OutboundHostInterfaceForTracking {
var t : Outbound? = nil
init() {
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
func calcFeatureVector(bitmap: Any?) -> KotlinArray<KotlinArray<KotlinFloatArray>>? {
do {
var test : Any? = (bitmap as! Bitmap).bitmap
return nil
} catch {
return nil
}
}
}
The TrackingWrapper looks like this:
class TrackingWrapper : NSObject {
static var instance: TrackingWrapper? = nil
var inbound: Inbound? = nil
var worker: Worker
override init() {
self.worker = Worker()
super.init()
initInboundInterface()
}
func initInboundInterface() {
runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
HostInterfaceForTracking()
}
}
func runOnMatchingLibraryThread(block: #escaping() -> Void) {
worker.enqueue {
block()
}
}
}
The function runOnMatchingLibraryThread is needed because every call to the TrackingLibrary needs to be called from the exact same thread, so the Worker class initializes a thread and enqueues every method to that thread.
The Bitmap in this case is simply a wrapper for an UIImage, which I already accessed with the .bitmap call, so I've tried to access the wrapped UIImage and save it in the test variable. The library gets the current camera frame from the Swift side every few frames and sends the current image wrapped as a Bitmap to the method calcFeatureVector depicted here.
Problem: My memory load starts increasing as soon as the app starts until the point it crashes. This is not the case if I don't access the wrapped UIImage (var test : Any? = (bitmap as! Bitmap)). So there is a huge memory leak, just by accessing the wrapped variable on the Swift side. Is there anything I've missed or is there any way to release the memory?
Looks like you have a circular dependency here:
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
You are asking a property inside HostInterfaceForTracking to maintain a strong reference to the same instance of HostInterfaceForTracking. You should be using [weak self] to avoid the circular reference.
EDIT:
Ok after seeing the rest of you code theres a lot to unpack. There is a lot of unnecessary bouncing back and forth between classes, functions and threads.
There is no need to use runOnMatchingLibraryThread to just create an instance of something. You only need to use that for the code processing the image itself (I would assume, I haven't seen anything so far that requires being split off into another thread). Inside TrackingWrapper, you can create a singleton more easily, and matching the swift pattern by simply doing this as the first line:
static let shared = TrackingWrapper()
And everywhere you want to use it, you can just call TrackingWrapper.shared. This is more common and will avoid one of the levels of indirection in the code.
I'm not sure what Worker or Inbound are, but again these can and should be created inside the TrackingWrapper init, rather than branching Inbound's init, to use another thread.
Inside initInboundInterface you are creating an instance of HostInterfaceForTracking() which doesn't get stored anywhere. The only reason HostInterfaceForTracking is continuing to stay in memory after its creation, is because of the internal circular dependency inside it. This is 100% causing some form of a memory issue for you. This should probably also be a property on TrackingWrapper, and again, its Init should not be called inside runOnMatchingLibraryThread.
Having HostInterfaceForTracking's init, also using runOnMatchingLibraryThread is problematic. If we inline all the code whats happening is this:
TrackingWrapper
init() {
self.runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
}
Having all these classes unnecessarily keep coming back to TrackingWrapper is going to cause issues.
Inside HostInterfaceForTracking 's init, no need to be creating Outbound on a separate thread. First line in this class can simply be:
var t : Outbound = OutBound()
Or do it in the init if you prefer. Either way will also remove the issue of needing to unwrap Outbound before using it.
Inside Outbound you are storing 2 references to the hostInterface instance:
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
I would have imagined there should only be 1. If there are now multiple copies of a class that has a circular dependency, which has multiple calls to separate threads. This again will cause issues.
I'm still not sure on the differences between Swift and Kotlin. In Swift when passing self into a function to be stored, the class storing it would mark the property as weak, like so:
weak var hostInterface: ......
Which will avoid any circular dependency from forming. A quick google says this isn't how things work in Kotlin. It might be better to look into the swift side passing in a closure (lambda on kotlin) and the kotlin side executing that. This might avoid the need to store a strong reference. Otherwise you need to be looking into some part of your code setting hostInterface back to null. Again its a bit hard to say only seeing some of the code and not knowing how its working.
In short, it looks like the code is very over complicated, and needs to be simplified, so that all these moving pieces can be tracked easier.
Here's a Swift class that uses ReactiveSwift, wrapping a MutableProperty in a Property, and adding a subscription to that Property in a ScopedDisposable:
class Leaker {
let mutableProperty = MutableProperty<Int>(0)
var wrapperProperty: Property<Int> {
return Property(self.mutableProperty)
}
private var disposable: ScopedDisposable<AnyDisposable>?
init() {
let disposable = CompositeDisposable()
disposable += self.wrapperProperty.producer
.startWithValues { value in
print("\(value)")
}
self.disposable = ScopedDisposable(disposable)
}
}
If I give another class a property of type Leaker?, and then set it using self.leaker = Leaker(), this creates a leak. By "creates a leak," I mean it sets off the Leaks instrument, showing a leaked object labeled Malloc 32 Bytes, with a stack trace that includes Leaker.init() calling Leaker.wrapperProperty.getter.
Why does this leak? I'm finding it hard to understand exactly what is causing the memory allocated here to never be released.
Some other facts that might be useful:
This doesn't leak if I subscribe to mutableProperty directly
This doesn't leak if I wrap mutableProperty in a lazy property instead of a computed property
This doesn't leak if I create a temporary Leaker, e.g. let _ = Leaker()
I tried, but was not able to reproduce the memory leak. I've used your exact Leaker class with this code:
final class OtherClass {
let leaker: Leaker?
init() {
self.leaker = Leaker()
}
}
var otherClass: OtherClass? = OtherClass()
var mutablePropertyCompleted = false
otherClass!.leaker!.mutableProperty.producer.startWithCompleted {
mutablePropertyCompleted = true
}
var wrappedPropertyCompleted = false
otherClass!.leaker!.wrapperProperty.producer.startWithCompleted {
wrappedPropertyCompleted = true
}
otherClass = nil
if(!mutablePropertyCompleted) {
print("LEAK")
}
if(!wrappedPropertyCompleted) {
print("LEAK")
}
One thing to note is that MutableProperty sends completed in its deinit method, so it should be possible to detect a leak the way I did here.
But just to be sure I also profiled this with the memory leaks instrument which detected nothing.
Since you mentioned some specific circumstances in which this does not leak for you it might just be that the way I'm trying to reproduce the issue is wrong, maybe you can post a complete example?
Anyway, I don't see an error in the example you posted, so either this is a bug in ReactiveSwift, or its a false positive by the profiler (which has happened with ReactiveSwift before as jjoelson pointed out in his comment)
Sorry, but I know this is a really dumb question, and I already kind of 'know' the answer, but I need someone to clearly explain to me WHY the answer is what it is.
Lately, I've become a bit obsessed/paranoid about retain cycles and memory leaks in my code, after going through some nightmarish debugging with various memory issues, so in the future I want to nip them in the bud. But after reading and learning a lot about ARC and retain cycles in Swift, although it makes sense, I still don't really have enough of an "intuitive" or natural feel for it, to feel confident that I could spot one, or the lack of one, as I'm coding. So I'm starting to become a little paranoid that I'm creating retain cycles with even basic stuff without realizing it.
So, with that in mind, why doesn't any ordinary function that uses a variable declared outside of it create a retain cycle? For example:
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
In this case, self.moreLetters references the function addLetters, and then the constant self.a is references from within the function addLetters. So would this create a retain cycle if I don't capture weak/unowned self? It seems absurd to me that something this simple would cause a problem...or is it? What about in a nested function, like this:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
Would that also create a retain cycle? (Yeah I know this is a convoluted way of performing a simple task; I'm just using this code as an example to make my point).
Have I become super-paranoid and am severely overthinking things?
First, you need to understand how a basic retain cycle is formed. A retain cycle is formed when an object A refers to an object B strongly and at the same time. object B refers to object A strongly as well.
Let's look at your first bit of code.
class someClass {
let a = "I'm letter a"
let moreLetters = addLetters()
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
}
Actually, a class by itself can never create retain cycles, so let's add some code to create an object:
var obj = someClass()
First, a is initialized to "I'm letter a". After that, moreLetters is initialized by calling the method addLetters. After the method returns, moreLetters is initialized to "I'm letter abcdefg". So far so good.
Now we set obj to nil:
obj = nil
If a retain cycle were formed, obj would not be deinitialized. However, in actuality, obj is deinitialized because nothing holds a strong reference to obj!
"Wait a minute!" you say, "But the method addLetters still refers to someClass because it has a in it!" Well, in fact, addLetters has already returned! Therefore, everything in it doesn't matter anymore! In addition, addLetters belongs to obj, which you have already set to nil!
Now let's look at your second code:
func someFunction () -> String {
let a = "I'm letter a"
func addLetters () -> String {
let newString = a + "bcdefg"
return newString
}
let moreLetters = addLetters()
return moreLetters
}
A retain cycle does not form because there isn't a even a reference type! There is no objects to be created. All you did in the second code is playing with strings, which are value types. Even if there were a class, a retain cycle would not form because as I said, when you set obj to nil, all the methods in it "disappear" because methods belong to objects.
What about those closures where I must write [weak self] or a retain cycle forms?
Those closures are escaping closures. Normal closures are deinitialized after they return. However, escaping closures are retained by some object so they are not deinitialized immediately. For more info, see Escaping Closures in Swift
I've been reading a bunch about Swift initilization lately, and one solution that appears to have some merit is utilizing lazy variables to avoid the doom and gloom of optionals, especially when extending UIViewControllers or other classes along those lines. With that in mind, I've got code that looks like this:
final class MyViewController : UIViewController {
lazy private var myOtherViewController: MyOtherViewController = MyOtherViewController()
deinit {
// myOtherViewController = nil // Can't do this. Expression resolves to an unused l-value
}
override func viewDidLoad() {
super.viewDidLoad()
myOtherViewController.foo();
}
}
// elsewhere...
final class MyOtherViewController : UIViewController {
deinit {
print("Deinit!")
}
}
In the example here it appears that MyOtherViewController's deinit method never seems to fire, but I also have no way to make myOtherViewController deinit. Setting it to nil isn't allowed. Is there something I'm missing that forces MyOtherViewController to retain? Or have I just been impatient with the garbage collector?
To do this, your lazy variable would have to be declared as optional even though you intend to initialize it as soon as the value is needed (i.e. lazily).
class MyObject {
func run() {
print( "MyObject :: run()" )
}
init() {
print( "MyObject :: init" )
}
deinit {
print( "MyObject :: deinit" )
}
}
class MyContext {
lazy var myObject:MyObject? = MyObject()
}
let myContext = MyContext()
myContext.myObject?.run() //< "MyObject :: init"
myContext.myObject = nil //< "MyObject :: deinit"
Also, I disagree with the notion of the "doom and gloom of optionals"—one only need know which of the many available techniques is most convenient and practical way handle them, and how to avoid allowing too many permutations of state to exist among combinations of optional and non-optional values in a given context. Furthermore, an optional is actually exactly what you want here because you intend to nullify it. Employing an optional doesn't mean that a value will be nil for now until you initialize it, but rather that it may be nil at any point in its life, even if it is defined at declaration or any other time beforehand. If avoiding optionals is truly that high of a priority for you, you'd have to take a step back and re-evaluate your architecture to create a situation where you no longer have the need to de-initialize the value.
I agree with Grimxn, that this pattern of setting that property to nil (and thus needing to make it optional in order to be able to do that) is unnecessary. When MyViewController is deallocated, it automatically releases its strong reference to MyOtherViewController.
The only time you need to manually nil this property is when you have to resolve a strong reference cycle (and if you had such cycle, you can't resolve that in deinit, but rather viewDidDisappear or something like that).
If you're not seeing this other view controller get deallocated, then I'd suggest finding the source of the strong reference cycle and resolving that.
I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash