Ive been able to distill a problem seen in an app I've written, and have reproduced it in a simple example.
Given these classes:
class Thing {
var name:String = ""
var price:Double = 0.0
var changed:Double = 0.0
var percentChanged:Double = 0.0
}
class TestUIViewController: UIViewController {
}
class ViewController: TestUIViewController {
var thing:Thing?
#IBAction func clicked(_ sender: AnyObject) {
self.thing = Thing()
}
}
I created a UIView with a button, that when pressed, a thing is instantiated. With the Instruments profiler up, I can see memory leaks occurring.
However, if the ViewController class extends from UIViewController, there are no issues.
This was all reproduced from a quick test app, so there are no other external forces at play here that i can think of.
Here is the example code - https://www.dropbox.com/s/ooqh77lhpzbvpv1/ArcTest.zip?dl=0
You may have found a bug in the leak detector, and it could be quite an interesting bug, so you should report it to Apple. But there is in fact no leak. I downloaded and ran your project under Instruments and clicked the button 10 times. This is what I saw in Instruments allocations template:
That is the expected result. There are 9 transient Things, and only one persistent Thing — the one currently assigned to the property. A leak would be if there were more than one persistent Thing, and there isn't.
Also, this is what the memory gauge looks like in Xcode:
We get a little rise (a kind of "mesa") when I repeatedly tap the button, but then we settle back down to the base level again.
Related
I have 5 tabs in UITabbar when I switching 1 > 2 on first time, It slow because in 2 have so much complex view and setup on viewDidLoad().
How can I make it faster or better ? Can I preload 2 when I open 1 ? or Can I show loading indicator when it loading view ?
There is too little context about you problem. The only thing which I can recomend you is to research problem using "Time profiler" instrument in XCode
(For XCode 12.5)
Press XCode -> Open Developer Tool -> Instruments
Then. Press "Time Profiler"
For ViewController with such code
import UIKit
class DelayViewController: UIViewController {
var i = 0
override func viewDidLoad() {
while i < 100000000 {
print("i is \(i)")
i += 1
}
}
}
We can see in "Time profiler" how much time takes print method
You might have too much tasks running on main queue. You should be only doing updating of UI on main queue. Are you fetching something from database, downloading pictures etc. in the method DispatchQueue.main.async { } ? If yes, try fetching these things from database on some other queue (for example DispatchQueue.global). You can also use UIActivityIndicator until your setup is done, and then update it on main queue.
I have a problem in my iPhone app when trying to instantiate an ARSCNView again, after destroying it.
In my ViewController I programmatically create an ARSCNView for motion capture interaction:
func addARSceneView() {
arSceneView = ARSCNView(frame: self.view.frame)
arSceneView.loops = true
arSceneView.session.delegate = self
self.view.addSubview(arSceneView)
arSceneView.session.run(ARBodyTrackingConfiguration())
}
When the user leaves this part of the app, I tear it down like this:
func removeARSceneView() {
arSceneView.session.pause()
arSceneView.pause(self)
arSceneView.session.delegate = nil
arSceneView.removeFromSuperview()
arSceneView = nil
}
Later, when I try to instantiate an ARSCNView for the second time using the first function above, it crashes with an EXC_BAD_ACCESS in the constructor:
I also tried to use a view from a xib which contains an ARSCNView but the same problem occurs, in that case in the init(coder) function of that view.
I found nothing on this problem, I guess usually developers only create an ARSCNView once.
TLDR: Turn "Metal API Validation" on in your scheme.
I found the culprit, after creating a sample project with only the ARSCNView, which did not have this problem. I started by stripping everything away from my original project until it was as barebones as the sample. That did not solve it, so I compared every little setting of the two, and behold: in the "Run" scheme of the original project, under "Diagnostics", I had "Metal – API Validation" ticked off. I don't remember when and why I did that; I assume it was some attempt to improve performance at one point. However, enabling this checkbox solved the problem completely.
How to minimize the app memory consumption in ARKit Scene after clicking on the back button?
Because every time when I open an ARview in my App, the memory usage is increasing.
When I go back then it does not free the memory and after opening an ARview again, the memory usages increases even further.
class ARViewController: UIViewController, ARSCNViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupScene()
setupFocusSquare()
}
func setupScene() {
sceneView.delegate = self
sceneView.session = session
sceneView.antialiasingMode = .multisampling4X
sceneView.automaticallyUpdatesLighting = false
sceneView.preferredFramesPerSecond = 60
sceneView.contentScaleFactor = 1.3
enableEnvironmentMapWithIntensity(25.0)
if let camera = sceneView.pointOfView?.camera {
camera.wantsHDR = true
camera.wantsExposureAdaptation = true
camera.exposureOffset = -1
camera.minimumExposure = -1
camera.maximumExposure = 3
}
}
func setupFocusSquare() {
focusSquare?.isHidden = true
focusSquare?.removeFromParentNode()
focusSquare = FocusSquare()
sceneView.scene.rootNode.addChildNode(focusSquare!)
textManager.scheduleMessage("TRY MOVING LEFT OR RIGHT", inSeconds: 5.0, messageType: .focusSquare)
}
}
The attached source code is not enough to answer how exactly resolve memory leak. I will describe steps how to detect leaks. So, you can add such information to question or maybe resolve an issue by yourself.
Xcode has several instrument to detect memory leaks:
Debug Memory Graph. Launch the app in debug mode. Navigate to the screen and back several times so memory growth. Then tap "Debug Memory Graph" button at debug toolbar.
So, you will see current memory graph of your app. Try to detect some anomaly at left bar:
Maybe you will see few View Controllers or Scene Views. Try to understand what retain this objects and use weak attribute if needed.
Use instruments by click Product -> Profile:
2.1. Select Leaks instrument:
Run the app by tap Record button (red circle) and make several navigations to screen with issue and back. At the bottom you will see some leaks if any. Sort them by size and make attention at Responsible Library. There should be your app name.
If you find something interesting you can select the row and see responsible stack trace.
2.2 Use Allocations instrument:
Run the app by tap Record button (red circle). Navigate to the screen with issue and tap back once. Then click Mark Generation button:
Navigate to the screen with issue and back once more. And click "Mark Generation" once more. Repeat this steps once more and you should see three generations at the bottom:
So you will see which objects are stay alive between back and forward. You can expand for example Generation B and see which object is retained. By click to some row your will see responsible code.
Depending on results at previous steps you may understand where problem is and add some code to resolve retain cycle or unnecessary retain of some object.
For this year's WWDC scholarship, the format required is a Swift playground. I'm building my playground in an app, where I have sliders in UITableViewCells connected to a SCNScene and SCNNode and SCNParticleSystem via a custom delegate. It works perfectly fine running as an app, running on the Mac, but when it comes to the running it in swift playgrounds on the iPad, the delegate method crashes after a constant number of iterations. I have determined it is not the method in the node itself, as I made this print("Hello") and it still crashed with the exact same 96 iterations. The node has been in both Source files and the main playground file. Any ideas or help would be greatly appreciated.
Delegate
protocol ReturnParameterDelegate {
func parameter1(value: Float)
func parameter2(value: Float)
func defaultParameter(value: Float)
}
Table View Cell
#objc private func returnValue() {
guard let delegate = delegate else { return }
print(self.parameter.title)
switch self.parameter.title {
case ParameterNames.parameter1:
delegate.parameter1(value: self.slider.value)
...
}
}
Scene
public func parameter1(value: Float) {
node.changeValue(value: value)
}
...
After many hours of work and trying this with other methods, I have found a simple solution: move code from the main .playground to separate .swift files in the Sources folder. While this shouldn't change anything, adding it to the sources made all of my methods work perfectly. This does complicate protocols and I replaced them with creating a global object instead, however. It does remove the crash after a constant number of iterations and it makes certain other functions work that were not previously (for example, changing the diffuse of a SCNNode).
I move everything to source file. And it works a little bit better.
I've been developing an app which was working perfectly fine until I added an MKMapView. If I navigate in the map for a broader area, dismiss the view controller with the map and add a new view controller, the app will crash with Xcode saying "Lost connection to iPhone".
I have searched online and I found it should be a memory issue. So I have used a lot of ways to clean the memory of MKMapView, including only storing one instance of the MKMapView in AppDelegate and cleaning it after ViewDidDisappear like this:
if let annotations = self.mapView?.annotations {
self.mapView?.removeAnnotations(annotations)
}
if self.mapView?.mapType == MKMapType.standard {
self.mapView?.mapType = MKMapType.hybrid
} else if self.mapView?.mapType == MKMapType.hybrid {
self.mapView?.mapType = MKMapType.standard
}
self.mapView?.delegate = nil
self.mapView?.removeFromSuperview()
self.mapView = nil
Even more strange is that when I was tracking the memory usage, while in the map, the memory usage can go up to 300+mb and it seems to clean itself while exploring more. However, there will be around 200mb left after I dismiss that view controller and when I'm adding another simple VC with just one UIIMageView, it crashes with the "Lost Connection" thing.
I'm new to memory management but I do have a screenshot here for instrument profile:
Just like here in the image. Generation A is the point where I presented the view controller with the MKMapView and Generation B is the point where I dismissed that view. The memory usage was apparently dropping, which is good. But as soon as I click on presenting a different view controller, it disconnects just like in Xcode.
I have done a lot of research on this and I really don't know what to do at this point. Thanks if you guys can help me out here!!!!
Problem fixed after I switched to Google Map...