Memory Access for class instances - swift

Consider the following code:
class ViewController: UIViewController {
var dummy = DummyClass()
override func viewDidLoad() {
super.viewDidLoad()
var copyDummy = dummy
if (copyDummy === dummy){
print("Same instance")
}
increment(&copyDummy)
}
func increment(_ number: inout DummyClass) {
number.change += dummy.change
}
}
class DummyClass {
var change = 10
}
Now from my understanding, both copyDummy and dummy point to the same memory location.
When I call func increment using increment(&dummy) there is a runtime error since I am trying to write and read the same memory location simultaneously.
But when I callincrement(&copyDummy) there is no error.
Why such behaviour if copyDummy is also accessing the same memory location as dummy variable?
Sorry if this turns to be a very basic question and/or if I have got my understanding incorrect. I am trying to wrap my head around this from quite some time.
Thanks in advance!
Modified code as per helpful suggestions:
class ViewController: UIViewController {
var dummy = DummyClass()
override func viewDidLoad() {
super.viewDidLoad()
increment(dummy)
}
func increment(_ number: DummyClass) {
number.change += dummy.change
}
}
class DummyClass {
var change = 10
}
Isnt func increment writing the same memory location ie. numbers.change from where its also reading ie. dummy.change

Related

How can I change a value inside a class from an outside function?

How can I change the value inside a class from an outside function?
class MyClass {
var number = 5
}
func changeNumber(){
number = 2
}
I'm more or less new to delegate and protocols, but as I understand it always happens between two classes. My problem is that that function runs when a certain note is played on a midi device and I need to change the text value of a label in a viewControler class.
welcome to StackOverflow! You could just change the numberProperty like this
class MyClass{
var number = 5
}
func changeNumber(_ myClass: MyClass){
myClass.number = 2
}
let myClassInstance = MyClass()
print(myClassInstance.number) //5
changeNumber(myClassInstance)
print(myClassInstance.number) //2
The conventional approach is to make the view controller the delegate:
final class MyViewController: UIViewController {
var number = 5
let midi = Midi()
func viewDidLoad() {
super.viewDidLoad()
midi.delegate = self
}
}
extension MyViewController: MidiDelegate {
func changeNumber(device: Midi, value: Int) {
number = value
}
}

How to use autopoolrelease in swift

The use of autoreleasepool{} in swift is as follows.
autoreleasepool {
// all usage here
}
But I do not know how to use it in public.
Example
class ViewController: UIViewController {
autoreleasepool {
var results: Results<Item>!
}
override func viewDidLoad() {
super.viewDidLoad()
.....
}
}
If this is public, an error occurs. Does autoreleasepool{} need to be in func only?

Testing Delegation in Playground giving 'nil'

I have the following code in Playground -I'm learning delegation-...
import UIKit
protocol FollowThisProtocol {
func passingTheValue(aValue: String)
}
class IPassTheValues{
var aDelegate: FollowThisProtocol!
func runThisFunc(){
aDelegate.passingTheValue(aValue: "I like this game")
}
}
class IReceiveTheValues: FollowThisProtocol{
var localString: String!
var instanceOfClass: IPassTheValues!
func runReceivefunc(){
instanceOfClass.aDelegate = self
}
func passingTheValue(aValue: String) {
localString = aValue
}
}
When I attempt to
print(IReceiveTheValues().localString)
it's giving me nil
It also gives me nil if I run the following lines before attempting to print(IReceiveTheValues().localString)...
IPassTheValues()
IReceiveTheValues()
could you please help me understand why the value is not being passed from the 1st class to the 2nd..?
Or if you can spot something in my code that is contradicting itself, could you please point it out..?
Appreciate your time and help.
You need to create the IPassTheValues object before assigning yourself as the delegate, and then call runThisFunc() on the instance:
func runReceivefunc(){
instanceOfClass = IPassTheValues()
instanceOfClass.aDelegate = self
instanceOfClass.runThisFunc()
}
Then test:
// Create the `IReceiveTheValues` object
let irtv = IReceiveTheValues()
// Run the method
irtv.runReceivefunc()
// Get the resulting string
print(irtv.localString)
I suggest 2 other changes. Make your delegate weak so that you don't get a retain cycle which makes it impossible to delete either object. In order to do that, you will need to add : class to your protocol declaration because only reference objects (instances of a class) can be weak.
Here's the modified code. Try it and see what happens when you delete weak.
protocol FollowThisProtocol: class {
func passingTheValue(aValue: String)
}
class IPassTheValues{
weak var aDelegate: FollowThisProtocol!
func runThisFunc(){
print("Calling delegate...")
aDelegate.passingTheValue(aValue: "I like this game")
}
deinit {
print("IPassTheValues deinitialized")
}
}
class IReceiveTheValues: FollowThisProtocol{
var localString: String!
var instanceOfClass: IPassTheValues!
func runReceivefunc(){
instanceOfClass = IPassTheValues()
instanceOfClass.aDelegate = self
instanceOfClass.runThisFunc()
}
func passingTheValue(aValue: String) {
print("Receiving value from helper object...")
localString = aValue
}
deinit {
print("IReceiveTheValues deinitialized")
}
}
func test() {
let irtv = IReceiveTheValues()
irtv.runReceivefunc()
print(irtv.localString)
}
test()

Using NSTreeController with NSOutlineView

I'm trying (unsuccessfully) to build a TreeController-controlled NSOutlineView. I've gone through a bunch of tutorials, but they all pre-load the data before starting anything, and this won't work for me.
I have a simple class for a device:
import Cocoa
class Device: NSObject {
let name : String
var children = [Service]()
var serviceNo = 1
var count = 0
init(name: String){
self.name = name
}
func addService(serviceName: String){
let serv = "\(serviceName) # \(serviceNo)"
children.append(Service(name: serv))
serviceNo += 1
count = children.count
}
func isLeaf() -> Bool {
return children.count < 1
}
}
I also have an even more simple class for the 'Service':
import Cocoa
class Service: NSObject {
let name: String
init(name: String){
self.name = name
}
}
Finally, I have the ViewController:
class ViewController: NSViewController {
var stepper = 0
dynamic var devices = [Device]()
override func viewDidLoad() {
super.viewDidLoad()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func addDeviceAction(_ sender: Any) {
let str = "New Device #\(stepper)"
devices.append(Device(name: str))
stepper += 1
print("Added Device: \(devices[devices.count-1].name)")
}
#IBAction func addService(_ sender: Any) {
for i in 0..<devices.count {
devices[i].addService(serviceName: "New Service")
}
}
}
Obviously I have 2 buttons, one that adds a 'device' and one that adds a 'service' to each device.
What I can't make happen is any of this data show up in the NSOutlineView. I've set the TreeController's Object Controller Property to Mode: Class and Class: Device, and without setting the Children, Count, or Leaf properties I get (predictably):
2017-01-04 17:20:19.337129 OutlineTest[12550:1536405] Warning: [object class: Device] childrenKeyPath cannot be nil. To eliminate this log message, set the childrenKeyPath attribute in Interface Builder
If I then set the Children property to 'children' things go very bad:
2017-01-04 17:23:11.150627 OutlineTest[12695:1548039] [General] [ addObserver:forKeyPath:options:context:] is not supported. Key path: children
All I'm trying to do is set up the NSOutlineView to take input from the NSTreeController so that when a new 'Device' is added to the devices[] array, it shows up in the Outline View.
If anyone could point me in the right direction here I'd be most grateful.
Much gratitude to Warren for the hugely helpful work. I've got it (mostly) working. A couple of things that I also needed to do, in addition to Warren's suggestions:
Set the datastore for the Tree Controller
Bind the OutlineView to the TreeController
Bind the Column to the TreeController
Bind the TableView Cell to the Table Cell View (yes, really)
Once all that was done, I had to play around with the actual datastore a bit:
var name = "Bluetooth Devices Root"
var deviceStore = [Device]()
#IBOutlet var treeController: NSTreeController!
#IBOutlet weak var outlineView: NSOutlineView!
override func viewDidLoad() {
super.viewDidLoad()
deviceStore.append(Device(name: "Bluetooth Devices"))
self.treeController.content = self
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func addDeviceAction(_ sender: Any) {
if(deviceStore[0].name == "Bluetooth Devices"){
deviceStore.remove(at: 0)
}
Turns out the Root cannot be child-less at the beginning, at least as far as I can tell. Once I add a child, I can delete the place-holder value and the tree seems to work (mostly) as I want. One other thing is that I have to reload the data and redisplay the outline whenever the data changes:
outlineView.reloadData()
outlineView.setNeedsDisplay()
Without that, nothing. I still don't have the data updating correctly (see comments below Warren's answer) but I'm almost there.
To state the obvious, a NSTreeController manages a tree of objects all of which need to answer the following three questions/requests.
Are you a leaf i.e do you have no children? = leafKeyPath
If you are not a leaf, how many children do you have ? = countKeyPath
Give me your children! = childrenKeyPath
Its simple to set these up in IB or programatically. A fairly standard set of properties is respectively.
isLeaf
childCount
children
But its totally arbitrary and can be any set of properties that answer those questions.
I normally set up a protocol named something like TreeNode and make all my objects conform to it.
#objc protocol TreeNode:class {
var isLeaf:Bool { get }
var childCount:Int { get }
var children:[TreeNode] { get }
}
For your Device object you answer 2 out 3 question with isLeaf and children but don't answer the childCount question.
Your Device's children are Service objects and they answer none of that which is some of the reason why you are getting the exceptions.
So to fix up your code a possible solution is ...
The Service object
class Service: NSObject, TreeNode {
let name: String
init(name: String){
self.name = name
}
var isLeaf:Bool {
return true
}
var childCount:Int {
return 0
}
var children:[TreeNode] {
return []
}
}
The Device object
class Device: NSObject, TreeNode {
let name : String
var serviceStore = [Service]()
init(name: String){
self.name = name
}
var isLeaf:Bool {
return serviceStore.isEmpty
}
var childCount:Int {
return serviceStore.count
}
var children:[TreeNode] {
return serviceStore
}
}
And a horrible thing to do from a MVC perspective but convenient for this answer. The root object.
class ViewController: NSViewController, TreeNode {
var deviceStore = [Device]()
var name = "Henry" //whatever you want to name your root
var isLeaf:Bool {
return deviceStore.isEmpty
}
var childCount:Int {
return deviceStore.count
}
var children:[TreeNode] {
return deviceStore
}
}
So all you need to do is set the content of your treeController. Lets assume you have an IBOutlet to it in your ViewController.
class ViewController: NSViewController, TreeNode {
#IBOutlet var treeController:NSTreeController!
#IBOutlet var outlineView:NSOutlineView!
override func viewDidLoad() {
super.viewDidLoad()
treeController.content = self
}
Now each time you append a Device or add a Service just call reloadItem on the outlineView (that you also need an outlet to)
#IBAction func addDeviceAction(_ sender: Any) {
let str = "New Device #\(stepper)"
devices.append(Device(name: str))
stepper += 1
print("Added Device: \(devices[devices.count-1].name)")
outlineView.reloadItem(self, reloadChildren: true)
}
Thats the basics and should get you started but the docs for NSOutlineView & NSTreeController have a lot more info.
EDIT
In addition to the stuff above you need to bind your outline view to your tree controller.
First ensure your Outline View is in view mode.
Next bind the table column to arrangedObjects on the tree controller.
Last bind the text cell to the relevant key path. In your case it's name. objectValue is the reference to your object in the cell.

variable cannot be modified in another class

am trying to modify a variable in another class, but it doesn't change.
class ViewController: UIViewController {
var t = 1
override func viewDidLoad() {
super.viewDidLoad()
t = 9
var pp = action().test()
println(pp) // got “2” here
}
the above should print "10" , but it shows "2".
another swift file:
class action {
var k = ViewController().t
func test()->Int{
k++
return k
}
}
Anything I made wrong?
Thnaks.
When you have a class with properties, and create an instance of that class, properties are bound to the class instance and not the class type. So if you create 2 instances of the same class their properties are independent, so if you change a property in one instance, that won't affect the same property in the other instance.
Note: by convention, in swift type names always start in uppercase, so I have renamed action to Action in my code.
Your code isn't working as expected because in the Action class you are creating a new instance of ViewController:
var k = ViewController().t
which has no relationship with the instance used to instantiate Action - so the new instance will have its t property set to 1.
The correct way to fix it is to pass the view controller instance to Action, and let it work on that instance.
class ViewController: UIViewController {
var t = 1
override func viewDidLoad() {
super.viewDidLoad()
t = 9
var action = Action(viewController: self)
var pp = action.test()
println(pp) // got “2” here
}
}
class Action {
var k: Int
init(viewController: ViewController) {
self.k = viewController.t
}
func test()->Int{
k++
return k
}
}
The above code should give an indication of what's wrong with your code, but it can be written in a better way. Action doesn't really need the ViewController instance, it just needs an integer passed in to its initializer, so a better way to achieve the same result is by modifying the code as follows:
class ViewController: UIViewController {
var t = 1
override func viewDidLoad() {
super.viewDidLoad()
t = 9
var action = Action(t: self.t)
var pp = action.test()
println(pp) // got “2” here
}
}
class Action {
var k: Int
init(t: Int) {
self.k = t
}
func test()->Int{
k++
return k
}
}