Is this a memory leak in following scenario in Swift Programming?
var string: String = "test-string-1" // statement-1
string = "test-string-2" // statement-2
Is it memory leak through execution from statement-1 to statement-2?
OR Do I this way?
var string: String? = "test-string-1"
string = nil
string = "test-string-2"
Please answer with proper description.
You don't need to do in the second way.
I guess the first scenario is safe enough.
After setting string to "test-string-2", the reference count of the "test-string-1" become 0. So, it will be deallocated by ARC.
You don't need to even think about a memory leak here, it is something very different from assigning a new value to a variable. It occurs when an instance is unable to be released from memory and is explained at length by Apple in the docs, but in brief here is the simplest scenario:
class Parent {
var child:Child?
}
class Child {
var parent: Parent?
}
var child = Child()
let parent = Parent()
child.parent = parent
parent.child = child // strong reference cycle created
A "strong reference cycle" has been created because the parent holds a reference to a child instance, and that same child instance holds a reference to the parent instance that holds a reference to it. The result is that it cannot be determined when to deinitialize either instance. This can be demonstrated by placing the code in a view controller and running it inside an app:
import UIKit
class Parent {
var child:Child?
deinit {
print("deinitialized")
}
}
class Child {
var parent: Parent?
deinit {
print("deinitialized")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let child = Child()
let parent = Parent()
child.parent = parent
parent.child = child // strong reference cycle created
}
}
Note that there are no console messages to indicate that deinitialization has taken place because it hasn't. Instead the instances both live on forever more. We call this a leak because the instances do not go out of existence but at the same time they are not reachable.
Now change one of the references to weak like so:
import UIKit
class Parent {
var child:Child?
deinit {
print("deinitialized")
}
}
class Child {
weak var parent: Parent?
deinit {
print("deinitialized")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let child = Child()
let parent = Parent()
child.parent = parent
parent.child = child // strong reference cycle created
}
}
You will see two deinit messages. The parent alone is now responsible for keeping the child "alive", so it can release the child when it goes out of existence automatically via ARC. There is no ambiguity over who owns who, even when an owned instance has a reference to its owner, because it is now weak (rather than strong).
Related
I'm new to Swift and I'm having a hard time understanding the purpose of assigning self to a delegate. Part of the difficulty stems from the fact that delegate seems to be used in two different ways.
First is as means to send messages from one class to another when a specific event happens, almost like state management. Second is to enable "a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type," as stated in documentation. I have a feeling that these two are fundamentally the same and I'm just not getting it.
protocol PersonProtocol {
func getName() -> String
func getAge() -> Int
}
class Person {
var delegate: PersonProtocol?
func printName() {
if let del = delegate {
print(del.getName())
} else {
print("The delegate property is not set")
}
}
func printAge() {
if let del = delegate {
print(del.getAge())
} else {
print("The delegate property is not set")
}
}
}
class ViewController: UIViewController, PersonProtocol {
var person: Person!
override func viewDidLoad() {
person.delegate = self
person.printAge()
person.printName()
}
func getAge() -> Int {
print("view controller")
return 99
}
func getName() -> String {
return "Some name"
}
}
What is the purpose of person.delegate = self in this case? Isn't ViewController already required to conform to PersonProtocol without it?
I have a feeling that these two are fundamentally the same
The first is a special case of the second. "send messages from one class to another" is just a specific way of "handing off some of its responsibilities". The "messages" are the "responsibilities"
What is the purpose of person.delegate = self in this case?
Here, person delegates (i.e. hands off) some of its responsibilities to another object. It does this by sending messages to another object. First, it needs to identify which objects it can delegate these responsibilities to. This is achieved by requiring that its delegate conform to PersonProtocol, as PersonProtocol defines the messages that Person is going to send.
Next, person needs to know exactly which object it should send these messages to. This is what person.delegate = self does. Remember that person doesn't know anything about your ViewController until this point. Instead of = self, you could say:
person.delegate = SomeOtherClassThatConformsToPersonProtocol()
and person will send its messages to that object instead, and the methods in your ViewController won't be called.
Isn't ViewController already required to conform to PersonProtocol without it?
Correct, but without it, person doesn't know which object it should send its messages to, and as a result, the methods in your ViewController won't be called.
Note that the delegate property should be declared as weak to avoid retain cycles. When you do person.delegate = self, you get a retain cycle: self has a strong reference to person, person also has a strong reference to self via the delegate property.
If you notice inside your Person class, delegate is nil. If you don't execute person.delegate = self, delegate will remain nil.
In other words, assigning ViewController to person.delegate allows Person to identify who the delegate is (i.e., have a reference to ViewController), and that way you can successfully execute statements like delegate?.getName() or delegate?.getAge() from the Person class.
that means Person is not able to getName() and getAge() so Person class delegate that to other DataSource.
Lets say the your view controller has a data source class PersonDataSource which deal with API to get this information So
class PersonDataSource: PersonProtocol {
func getAge() -> Int {
print("view controller")
return 99
}
func getName() -> String {
return "Some name"
}
}
so the view controller will looks like this
class ViewController: UIViewController {
var person: Person!
var personDataSource = PersonDataSource()
override func viewDidLoad() {
person.delegate = personDataSource
person.printAge()
person.printName()
}
}
I have defined a class called Person. This is my code:
class Person {
var closure: (() -> ())?
var name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
then I use Person in class called ViewController:
class ViewController: UIViewController {
var person = Person(name: "john")
let aStr = "john is a cute boy"
override func viewDidLoad() {
super.viewDidLoad()
person.closure = {
print("\(self.aStr)")
}
person.closure!()
}
}
In my opinion, the picture of memory about my code like this :
So, from above picture, in my opinion, it will cause strong reference cycle between the three instances, but I can not get any leak from Instruments, so I have some confusion.
Does this code cause strong reference cycle?
If not, when will ARC deallocate the instance of Person? the method named deinit in Person class is never called.
Yes, this's a typical retain cycle.
To solve this problem use [weak self] in your closure
person.closure = { [weak self] in
guard let strongSelf = self else { return }
print("\(strongSelf.aStr)")
}
To really create a leak.
I create a demo App. Root is a navController.
The navController has a root controller. Let's call it buttonController.
When you click button in the buttonController, it create your ViewController and push to navController.
When you click back button in navigation bar, the navController pop your ViewController instance.
Profile it, then you will see the leak and the retain cycle in Instruments.
Xcode default template of iOS App use a single page, which always retain your ViewController instance. If the ViewController instance is still used by the system, it's actually not a leak yet.
So push & pop show that leak for you.
I tried to do some research but couldn't figure it out, so is it possible to create a member for the class UIViewController, or any class for that matter?
In every single one of my UIViewController subclasses I declare the data member
userdata = [NSManagedObject]()
So I was wondering if I could declare the variable "userdata" inside the actual UIViewController class, either directly or through an external file.
You can simply create a sub-class of UIViewController which has the userdata property and then derive all of your view controllers from that class instead of UIViewController
Something like:
class BaseViewController:UIViewContrller {
var userdata = [NSManagedObject]()
}
class NewViewController:BaseViewController {
// Your sub view controller implementation goes here
}
You should use extensions.
extension UIViewController {
var userData : [NSManagedObject] {
get {
return [NSManagedObject]()
}
}
}
If you don't want every UIViewController to have that property, you will have to use subclassing.
class DataViewController:UIViewContrller {
var userdata = [NSManagedObject]()
}
class NewViewController:DataViewController {
// Do something stuff to the View here
}
You can use extensions if userData is a computed property:
extension UIViewController {
var userData: [NSManagedObject] {
get { return an array from somewhere else }
set { set the value to somewhere else }
}
}
If your property is not computed but stored, you must use a subclass:
class BaseViewController: UIViewController {
var userData: [NSManagedObject] = []
}
And make every VC of yours inherit this class. The disadvantage of using this approach is that your view controllers can't inherit any other class, like UITableViewController.
So here is the best method I came up with.
Create a protocol:
protocol MyVC {
var userData: [NSManagedObject] { get set }
}
Now make every VC of yours conform to this protocol. In every VC, just start typing userData and use enter to select the right completion that Xcode provides and the property will be automatically added for you. If you forgot to do this, the compilation will fail.
Flowing code, I did try to create an object assign object property to a functions. And after init object I did try assign it to nil. But object did not release (because deinit never called).
I think problem is strong reference cycle between property and owner object. If really has strong reference cycle here, how to resolve this problem when assign property directly with a function?
class MyClass {
var aProperty: (() -> ())?
init() {
// problem when assign property as a method
self.aProperty = aMethod
}
func aMethod() {
print("method!!!")
}
deinit {
print("MyClass is being deinitialized")
}
}
var instance: MyClass? = MyClass()
instance?.aProperty?()
instance = nil
You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition. A capture list defines the rules to use when capturing one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.
lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
from
Strong Reference Cycles for Closures
In your case you should apply capture list when assigning a method to the property like this
init() {
self.aProperty = { [unowned self] in self.aMethod() }
}
You can still use a capture list to prevent the cycle. Just wrap the method call in a closure as shown in the code below.
class MyClass {
var aProperty: (() -> ())?
init() {
// use a capture list to prevent a reference cycle
self.aProperty = { [unowned self] in self.aMethod() }
}
func aMethod() {
print("method!!!")
}
deinit {
print("MyClass is being deinitialized")
}
}
var instance: MyClass? = MyClass()
instance?.aProperty?()
instance = nil
This eliminates the strong reference cycle in my testing.
I have a managed object defined:
#objc (Game)
class Game: NSManagedObject {
#NSManaged var player1: Player
#NSManaged var player2: Player
#NSManaged var totalScore: NSNumber
#NSManaged var gameDate: NSDate
}
I initialize it from ViewControllerA, then I give it to ViewControllerB using the delegate pattern. The protocol looks like this:
protocol gameProtocol {
func gameFunction(input: Game)
}
ViewControllerB signs up for the protocol:
class ViewControllerB: UIViewController, gameProtocol {...}
and implements this function to conform:
func gameFunction(input: Game) {
let currentGame = input
}
Then ViewControllerA can send a Game object to VCB as follows:
var gameDelegate: gameProtocol!
gameDelegate.gameFunction(myInitializedAndSavedGameObject)
This all works, but I need a class level variable inside ViewControllerB so that other code can be written to depend on the game. This, of course, does not work:
var currentGame = Game()
func gameFunction(input: Game) {
currentGame = input
}
I don't know the right words for it, but I think I want an initialized, empty Game object. I suppose I could write a convenience init that makes a temporary Game, but that doesn't seem like a good idea.
My current workaround is to have an NSManagedObjectID() and then recreate the object from the ID. But this is a lot of repeated code to get at an object that is central to what this ViewController is designed to work with.
So you want to push your NSManagedObject to your second View Controller?- You dont need an delegate, you could send your object as instance variable, as Wain already said.
For Example (in your MainViewController)
class MainViewControntroller: UIViewController {
var currentGameObject:YOURNSMANAGEDOBJECT!
func viewDidLoad() {
// load your Object
var fetchRequest = NSFetchRequest(entityName: "Game")
.....
games = context.executeFetchRequest(fetchRequest, error: nil) as [YOURNSMANAGEDOBJECT]
if(games.count > 0) {
// set to any game object (here is only the last)
currentGameObject = games.last?
}
}
}
// Initialize your Second View Controller (for example when using segues)
if(segue.identifier == "yourIdentifierInStoryboard") {
var yourNextViewController = (segue.destinationViewController as yourNextViewControllerClass)
yourNextViewController.currentGameObject = currentGameObject
So you are able to use your NSManagedObject in your SecondViewController - if you want to push it back, you could use a delegate.
'lazy var currentGame = Game()' seems to be what I want. By making the var lazy, the designated initializer is never incorrectly called. I am certain that the first thing to touch the var will be the gameFunction method, so my other code will compile and it won't crash at runtime. Alternative suggestions welcome.