I don't understand this concept that a closure captures data.. Can someone write a sample code using closures that show how data never gets destroyed.. I already read the Apple documents and I'm still confused. And also how does 'unowned' and 'weak' make any difference in a closure...
class TableViewController: UITableViewController {
var allWords = [String]()
var usedWords = [String]()
override func viewDidLoad() {
super.viewDidLoad()
if let allWordsPath = Bundle.main.path(forResource: "start", ofType: "txt"){
if let startWords = try? String(contentsOfFile: allWordsPath){
allWords = startWords.components(separatedBy: "\n")
}else{
allWords = ["Cake"]
}
startGame()
}
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Make Word", style: .plain, target: self, action: #selector (makeWord))
}
func startGame(){
allWords = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: allWords) as! [String]
title = allWords[0]
usedWords.removeAll(keepingCapacity: true)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return usedWords.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Word", for: indexPath)
cell.textLabel?.text = usedWords[indexPath.row]
return cell
}
func makeWord() {
let ac = UIAlertController(title: "Add Word", message: nil, preferredStyle: .alert)
ac.addTextField(configurationHandler: nil)
let submit = UIAlertAction(title: "Submit", style: .default){ [unowned self,ac]
(action: UIAlertAction!) in
let answer = ac.textFields?[0]
self.submit(answer: (answer?.text)!)
}
ac.addAction(submit)
present(ac,animated: true)
}
var number = 10
func submit(answer: String){
usedWords.append(answer)
tableView.reloadData()
}
how does unowned work here if we are not explicitly deallocating things..
You should first search for the difference between strong, weak, and unowned. There are PLENTY of answers here on SO about it.
Anyways, in this specific case:
Your closure has this code:
[unowned self,ac]
This is called a "capture list". It indicates the things that should be "captured" by value WHEN THE BLOCK is created. (if you don't specify them here, and you change the value somewhere after the block, the value inside the block would also be changed).
The reason why self is unowned and doesn't require to be deallocated is because unowned means:
"Don't worry about memory management for this variable, it will ALWAYS
have a value for the duration of my closure"
So, going back to unowned self, the reason you should declare weak or unowned the self variable from a closure is because if not you would create a retain cycle. Things can't be deallocated as long as something is referencing them. So in this case, your TableViewController is keeping your closure alive, and your closure is keeping your TableViewController alive. So because they are referencing each other none of them can get deallocated properly. -> Memory Leak
So we can conclude that self has to either be weak or unowned. For all intents and purposes in this example they are exactly the same. They both serve the purpose of "breaking the retain cycle" by removing the ability of the closure to keep self alive. So who will dealloc self you ask? your closure doesn't care. But think outside your closure. Your closure is being called by your TableViewController, so since there's no weird things going on here we can safely assume that, if there is an alert being shown it must definitively be shown over your TableViewController. So once you dismiss the alert or whatever, your TableViewController will keep working as usual. One you dismiss your TableViewController though, self WILL be deallocated (since the closure is referencing it as unowned), but by this point there is no way the alert is being shown. If you however do some weird things that make your TableViewController be dismissed WHILE the alert is still being shown, then once the user "submits" your app will crash. BECAUSE by declaring your variable unowned you basically made a promise to your closure that it wouldn't have to worry about the self entity, as it would always exist as long as your closure was alive.
Check this out. I'm creating 2 objects of the same kind. One has a reference to a closure that retains itself, so even if the function that created it goes out of scope, the object and the closure retain each other and never get released. The second object's closure has a weak reference to the object, and so when the object creating function goes out of scope, the reference count is 0, and when it gets released it releases the closure as well.
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
class B {
deinit {
print("\(name) deinit")
}
var name: String
init(name: String) {
self.name = name
}
var zort: (() -> ())?
func someMethod() {
print("")
}
}
func createStuffThatNeverGoesAway() {
var b: B = B(name: "bad");
b.zort = {
b.someMethod()
}
}
func createStuffThatGoesAway() {
var b: B = B(name: "good");
b.zort = { [weak b] in
b?.someMethod()
}
}
createStuffThatNeverGoesAway()
createStuffThatGoesAway()
Output:
good deinit
Related
I've got my view controller setup like:
class HomeViewController: UIViewController {
unowned var viewModel: HomeViewModelInterface!
private lazy var collectionViewDataSource: UICollectionViewDiffableDataSource<Segment, Int> = {
configureCollectionViewDataSource()
}()
// ...
// ...
func configureCollectionViewDataSource() -> UICollectionViewDiffableDataSource<Segment, Int> {
let dataSource = UICollectionViewDiffableDataSource<Segment, Int>(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
let cell = cell(with: HomeItemCollectionViewCell.self, for: indexPath, in: collectionView)
cell.configure(with: itemIdentifier, delegate: viewModel)
return cell
}
return dataSource
}
}
The problem occurs inside the cellProvider closure where it always says the unowned reference view model was already deallocated. I'm trying to understand why and also how to fix this. I know the cell provider closure is an escaping closure, maybe that has something to do with it that it could be called long after the function has returned and therefore not be able to access the view model any more? Does anyone know how I get around this?
The viewModel is set as a class instance from elsewhere just FYI.
Any help appreciated.
I set the Show Charts button on the DetailView Controller which triggers the getChartData function and shows me the values in display view in charts, now I want to call that function in the didselectrow on the main Viewcontroller so that the chart is loaded automatically, but it fails.
When I tried to call that function in didselectrow (DVC.getChartsData) I got the error "Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value"
DVC.getChartsData
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
ViewController:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let DVC = Storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
DVC.getDetailName = coin[indexPath.row].name
let formatedRoundingPrice = (coin[indexPath.row].price as NSString).floatValue * currencymodel.indexValue
let formatedPrice = String (format: "%.3f", formatedRoundingPrice)
DVC.getDetailPrice = formatedPrice
self.navigationController?.pushViewController(DVC, animated: true)
let percentage = String ((coin[indexPath.row].percent as NSString).floatValue)
DVC.getDetailPercent = percentage
tableView.deselectRow(at: indexPath, animated: true)
//DVC.getChartData()
}
DetailViewController:
#IBAction func tapLineChart(_ sender: Any) {
getChartData()
}
func getChartData () {
let chart = HITLineChartView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: displayView.bounds.height))
displayView.addSubview(chart)
let max = String((priceResult.max() ?? 0.0).rounded(.up))
let min = String((priceResult.min() ?? 0.0).rounded(.down))
let maxChange = abs((listOfChanges.max()) ?? 0.0).rounded(.up)
let minChange = abs((listOfChanges.min()) ?? 0.0).rounded(.up)
absMaxPercentage = Int(maxChange > minChange ? maxChange : minChange)
titles = ["\(getDetailName) closing price is \(getDetailPrice)"]
print(data)
chart.draw(absMaxPercentage,
values: listOfChanges,
label: (max: max, center: "", min: min),
dates: namesArray,
titles: titles)
addCloseEvent(chart)
finalURL = baseURL + "bitcoin" + "/market_chart?vs_currency=usd&days=5"
print(finalURL)
getBitcoinData(url: finalURL)
}
How to load my charts tap on a specific tableview cell instead of tapping on tapLineChart.
https://imgur.com/fg2502P
https://imgur.com/C4AzaRY
https://imgur.com/jOrwujy
if you want to call a function on viewControllerB that you declare from viewController A.
just create the object of the class file you want to use the function from
var obj mainVC = MainViewController()
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func commonMethod() {
print("From the main class")
}
}
Using that object, call the function in another file where you mean to use it
class OtherViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
mainVC.commonMethod()
// Do any additional setup after loading the view, typically from a nib.
}
}
Additionally, You can also create a new swift file, name it Global.swift, create all your functions that you want to use throughout the application here. They become "global functions"
You will want to use delegates or observers to pass data between view controllers.
I'm new to tutorials, but I wrote a bit about this here: https://www.eankrenzin.com/swift-blog/pass-data-throughout-your-app-with-observers-and-notifications-xcode-11-amp-swift-5
You should use optional binding to unwrap your VC let DVC = Storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
Your code is crashing because of that line. Check your interface builder to make sure the identifier is correct. Edit: this line was not causing a crash, but it is still better to use optional binding.The line is: https://imgur.com/CVP1x6H
NOTE: It is terrible practice to litter your app with instances when delegates and observers could work. Also do NOT have globals. Globals are disastrous for debugging and create tech debt.
I noticed this today when playing with NSOutlineView and NSTableHeaderCell, but when this specific configuration is made, an error/warning(?) is printed:
objc[2774]: Attempted to unregister unknown __weak variable at 0x1016070d0. This is probably incorrect use of objc_storeWeak() and objc_loadWeak(). Break on objc_weak_error to debug.
here's the snippet:
class Foo: NSCell {
weak var weak: NSView?
override func copy(with zone: NSZone? = nil) -> Any {
// according to NSCopying documentation:
// If a subclass inherits NSCopying from its superclass and declares
// additional instance variables, the subclass has to override copy(with:)
// to properly handle its own instance variables, invoking the superclass’s implementation first.
let copy = super.copy(with: zone) as! Foo
// this produces "Attempted to unregister unknown __weak variable"
copy.weak = self.weak
return copy
}
}
let view = NSView(frame: NSRect.zero)
let foo = Foo()
foo.weak = view
let copy = foo.copy() as! Foo
this also happens if I substitute NSCell with: NSEvent, NSImage, NSImageCell
but this doesn't happen to NSColor, NSDate, NSIndexPath
I started learning Swift without prior knowledge of Obj-C. could someone help me understand why this is? is it safe to ignore? who has the blame in this case?
This is a framework bug. It's easy to reproduce with the following crasher:
import Cocoa
class Cell: NSCell {
var contents: NSString?
override func copy(with zone: NSZone? = nil) -> Any {
let newObject = super.copy(with: zone) as! Cell
newObject.contents = contents
return newObject
}
}
func crash() {
let cell = Cell()
cell.contents = "hello world"
cell.copy() // crashes while releasing the copied object
}
crash()
When you use a weak var instead, you get the error message that you showed.
My gut feeling is that there is something in the copy implementation of NSCell (and possibly of NSEvent and NSImage) that does not handle subclassing for types that have non-trivial constructors. Accordingly, if you change let newObject = super.copy(...) with let newObject = Cell(), the crash is avoided. If your superclass's copy logic is simple enough, you should probably do that for now.
If you hit this problem, you should file a bug report separately of mine, but you can probably reuse my sample.
I have a class object in the controller, and then I have a closure in this object.
I assign a function of the controller to the object's closure, and then the page does not deinit.
How can I solve this problem?
import UIKit
class SecondViewController: UIViewController {
let test = TestObject()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.test.select = self.selectButton(index:)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.test.doSomethine()
}
func selectButton(index:Int){
print(index)
}
deinit {
print("deinit")
}
}
import UIKit
typealias selectBtnBlock = (_ index:Int)->()
class TestObject: NSObject {
var select:selectBtnBlock?
func doSomethine(){
self.select!(1)
}
}
This is because your test object's select closure strongly captures your SecondViewController when you do the following:
self.test.select = self.selectButton(index:)
I recommend you do some reading about weak and strong types via Apple's Swift language reference. The "interesting phenomenon" you encountered is called a strong reference cycle.
Essentially, since Swift uses ARC as its memory management model, any object that is referenced by at least one other object else will be kept alive, and its memory not deallocated.
In your case, test has captured its parent SecondViewContoller via the line I mentioned. What that means is you have a situation like the following:
SecondViewController -> (owns) test // since its a member of the class
test -> (strongly captures) SecondViewController // via the assignment
This causes a strong reference cycle between the two, and does not allow ARC to deallocate either.
When it (ARC) tries to free up test, is knows that SecondViewController references it, so it can be freed only if the parent is also freed. When it tries to deallocate SecondViewController, ARC knows that this object is referenced by test.select closure.
Since both have a reference count greater than one, neither will get deallocated.
One way to solve your issue is to write:
self.test.select = {
[weak self] // weakly capture self, this prevents a ref cycle
(i:Int)->() in // a closure that accepts an Int
guard let s = self else { return } // check if self is not nil
s.selectButton(index: i) // finally invoke the required method
}
Another way, similar intent:
self.test.select = { [weak self] i in
self?.selectButton(index: i)
}
The weak keyword in this context is used to tell the Swift compiler that I do not want to keep a strong reference to what I am capturing (self in this case).
Swift Closure will have a strong reference cycle when it refers to self like this example:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
}
In the previous example, I created a strong reference cycle so I have to fix it with:
class Test {
var name = "Hello"
func doSomething() {
{[unowned self] () -> Void in
self.name = "otherName"
}()
}
}
Question: If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
Neither. In most cases, just refer to self normally and do nothing with its memory management. You only have to worry about memory management if there is a danger of a retain cycle, and unless you store the closure somewhere, such as a property of self, there is no such danger.
You can easily prove this by adding a deinit implementation:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
deinit {
println("bye")
}
}
Now make a Test instance and immediately release it:
func testTest () {
let t = Test()
}
You see "bye" in the console, proving that the instance was released in good order. There was never any kind of "strong reference cycle" in this code. Your concerns are groundless.
[By the way, you are using the word "closure" wrong. Every Swift function is a closure. If there were a retain cycle issue merely because of using the word self in a closure, every Swift function would be subject to this issue - and clearly that is not the case. The place where weak and unowned self comes into play is in an anonymous function - and only, as I said before, if that anonymous function is itself also retained by self.]