Leaks in Swift 3 / iOS 10
I had the same problem and spent a lot of time digging. I found that if you create a Swift object from Objective-C code and the Swift object has a native Swift dictionary property, you will see this leak. It won't happen if all the code is Swift, and more usefully, it won't leak if you change the native Swift dictionary to an NSDictionary. This also applies to Swift Set's and NSSet's. I also saw that the leak happens on iOS 10 and not on iOS 9.
// LeakySwiftObject.swift
class LeakySwiftObject: NSObject {
let dict = [String: String]() // <- Any native Swift dictionary will reproduce the leak
}
// ObjectiveCObject.h
@class LeakySwiftObject;
@interface ObjectiveCObject : NSObject
@property (strong) LeakySwiftObject *leaky;
@end
// ObjectiveCObject.m
@implementation ObjectiveCObject
- (instancetype)init
{
self = [super init];
if (self) {
self.leaky = [LeakySwiftObject new];
}
return self;
}
@end
// ViewController.swift
class ViewController: UIViewController {
let testObj = ObjectiveCObject()
}
The Leaks Instrument reports a leak:
_NativeDictionaryStorageImpl<String,String
>
_NativeDictionaryStorageOwner<String,String
>
Swift 3 & iOS 10 false memory leak bug
I've tried different variations to confirm it's indeed a bug and my findings are:
- When you don't assign
c
in the sample code tosomeProperty
, both instances will print the string in their respectivedeinit
s. A true strong reference cycle won't deinit. - When
SomeClass
doesn't inherit fromNSObject
, this bug doesn't happen. - When using Swift 2.2, this doesn't happen.
- When using iOS 9-, this doesn't happen.
- Once
someProperty
is set tonil
somewhere in the code, both instances aredeinit
ed. Xcode 8 memory debugger confirms there are no memory leaks. However in Instruments, this change isn't reflected--rightfully so, since a real memory leak probably won't be resolved.
FYI, this doesn't happen only when it's assigned to a property of UIViewController
. I originally found out about this behavior in a singleton object.
Swift 3 enums leak memory when the class contains an array
Hey @endavid managed to replicate the issue consistently. We spend a good time trying to figure out what was going on and your post helped a lot!
Here is the sample repo: https://github.com/Giphy/ios-memory-leak-sample
Radar: https://openradar.appspot.com/radar?id=4992108083544064
We are developing SDKs and same exact issue surfaced with a small difference. Since we wanted things to interop we added @objc
to the enum definition and things started to leak exactly the way you described given your class has two properties, one enum and one mutable array.
Consistently reproduced the leak:
// Without @objc this enum won't leak
// however when this enum is included in a class
// which contains an array, it will leak
@objc enum leakingObjCMarkedEnum: Int {
// Just some random cases.
case apple, orange
}
// Wrapper class which contains an enum and Array
// The class needs to contain the the Array in order for
// the Enum to leak.
class WrapperClass {
// Optional enums marked with @objc will leak.
var leakyOptionalEnum: leakingObjCMarkedEnum?
// Include an array to trigger this behaviour.
// Empty arrays won't cause the leak, so lets add an arbitrary Int
var myArray: [Int] = [80]
}
class ViewController: UIViewController {
// Hang on to a reference to our Wrapper Class instance.
var wc: WrapperClass?
override func viewDidLoad() {
super.viewDidLoad()
// Allocate an instance of our class
// and things will start leaking at this point.
wc = WrapperClass()
}
}
Work Around:
If we convert the optional enum class property to a non-optional, leak will disappear.
// Let's convert the optional property to a non-optional
var leakyOptionalEnum: leakingObjCMarkedEnum = .orange
Edit:
It is fixed by guys @ Apple:
https://bugs.swift.org/browse/SR-5625
PR: https://github.com/apple/swift/pull/11341
MKMapView Instruments Memory Leak in iOS10
This was a bug and had been reported to Apple. It appears to have been corrected in iOS 11.
iOS10: How to avoid memory leaks in a segue circle
Presenting/Dismissing
NOTE:
If you are working with a navigation controller, you might want to check my answer about Pushing/Popping.
The action of "Back To Page1" in the fourth View Controller-, should be similar to:
@IBAction func backToPage01Tapped(_ sender: Any) {
presentingViewController?.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
}
Note that the reason of why the number of presentingViewController are three, because it's the number of previous View Controllers for the fourth one.
If you have only two previous View Controllers, then you must chain backwards twice and call dismiss from two View Controllers back:
presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
and so on...
Xcode 10.2 detects memory leaks in a new single view app
The question is solved with the new Xcode 10.3 release!
Resolved an issue where running an app in iOS 12.2 or later under the
Leaks instrument resulted in random numbers of false-positive leaks
for every leak check after the first one in a given run. (48549361)
Related Topics
Is There a Daylight Savings Check in Swift
For-In Loop and Type Casting Only for Objects Which Match Type
Accessor Gives the Wrong Value in Swift 1.2/2.0 Release Build Only
Converting Swift 2.3 to Swift 3.0 - Error, Cannot Invoke 'Datatask' with an Argument List of Type'
Nsjsonserialization Not Working as Expected in a Playground
No Code Signature Found After Pod Installed in Xcode 8
Output Compile Durations for Swift Files
Singleton and Init with Parameter
How to Program a Nsoutlineview
Why Is the Swift Compiler Marking This as an Error
How to Use Trailing Closure in If Condition
How to Find the Operator Definition in Swift
Index Out of Range in Swift with Removeatindex
Swiftui Present Alert with Input Field
Difference Between Type Method and Type Instance Method, etc