Swift How to "Pass by Value" of a Object

Swift how to pass by value of a object

If your class is subclass of NSObject,better to use NSCopying

class Fraction:NSObject,NSCopying{
var a:Int
var b:NSString?
required init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copyWithZone(zone: NSZone) -> AnyObject {
let theCopy=self.dynamicType.init(a: self.a)
theCopy.b = self.b?.copy() as? NSString
return theCopy
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
f.b = "after"
}

}

var object = Fraction(a:10)
object.b = "before"
print("before run func = " + object.toString())
print(object.b!) //“Before”

XXXclass.A_plusplus(object.copy() as! Fraction)
print("after ran func =" + object.toString() )
print(object.b!)//“Before”

If it is just a common swift class,You have to create a copy method

class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copy()->Fraction{
return Fraction(a: self.a)
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
}
}
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object.copy())
print("after ran func =" + object.toString() )

To make it clear,you have to know that there are mainly two types in swift

  1. Reference types. Like Class instance,function type
  2. Value types,Like struct and others(Not class instance or function type)

If you pass in a Reference types,you pass in the copy of Reference,it still point to the original object.

If you pass in a Copy type,you pass in the copy of value,so it has nothing to do with the original value

Let us talk about inout,if you use it,it pass in the same object or value.It has effect on Value type

func add(inout input:Int){
input++
}

var a = 10
print(a)//10
add(&a)
print(a)//11

Is Swift Pass By Value or Pass By Reference

Types of Things in Swift

The rule is:

  • Class instances are reference types (i.e. your reference to a class instance is effectively a pointer)

  • Functions are reference types

  • Everything else is a value type; "everything else" simply means instances of structs and instances of enums, because that's all there is in Swift. Arrays and strings are struct instances, for example. You can pass a reference to one of those things (as a function argument) by using inout and taking the address, as newacct has pointed out. But the type is itself a value type.

What Reference Types Mean For You

A reference type object is special in practice because:

  • Mere assignment or passing to function can yield multiple references to the same object

  • The object itself is mutable even if the reference to it is a constant (let, either explicit or implied).

  • A mutation to the object affects that object as seen by all references to it.

Those can be dangers, so keep an eye out. On the other hand, passing a reference type is clearly efficient because only a pointer is copied and passed, which is trivial.

What Value Types Mean For You

Clearly, passing a value type is "safer", and let means what it says: you can't mutate a struct instance or enum instance through a let reference. On the other hand, that safety is achieved by making a separate copy of the value, isn't it? Doesn't that make passing a value type potentially expensive?

Well, yes and no. It isn't as bad as you might think. As Nate Cook has said, passing a value type does not necessarily imply copying, because let (explicit or implied) guarantees immutability so there's no need to copy anything. And even passing into a var reference doesn't mean that things will be copied, only that they can be if necessary (because there's a mutation). The docs specifically advise you not to get your knickers in a twist.

Passing class by value

I don't see any real problem with mutability. Note that with classes, without passing-by-value, you just cannot use one operator to define the other one.

class Foo<T: Numeric> {
var value: T

init(value: T) {
self.value = value
}

static func + (lhs: Foo, rhs: Foo) -> Foo {
return Foo(value: lhs.value + rhs.value)
}

static func += (lhs: Foo, rhs: Foo) {
lhs.value += rhs.value
}
}

let ten = Foo<Int>(value: 10)
let eighteen = ten + Foo<Int>(value: 8)
eighteen += Foo<Int>(value: 1)

How to pass Object / Class as function's parameter

It is possible indeed. However you should check you code there because your using the attacking cat's current life instead of the target's to compute the remaining life:

func thunderClaw(otherCat : ninjaCat){
otherCat.health = health - self.attack
}

should be

func thunderClaw(otherCat : ninjaCat){
otherCat.health = otherCat.health - self.attack
}

or simply

func thunderClaw(otherCat : ninjaCat){
otherCat.health -= self.attack
}

Passing parameter value to Observable object

Initialize it inside init

struct ContentView: View {
var clientId: String
@ObservedObject var api: UnsplashAPI

init(clientId: String){
self.clientId = clientId
self.api = UnsplashAPI(clientId: clientId) // << here !!
}
// ...

Passing a value type as reference in Swift

structs are always passed by value. The whole point of using a struct is to have it behave as a value type. If you need delegation (which usually implies mutable state), you should be using a class.

If you really really need to, you could force pass-by-reference by using an inout parameter, but that is not recommended in general. You could also use a box type to simulate passing by reference. But, in general, you should just use a class if you need reference behavior.

SwiftUI pass by reference a class object to another view

Okay, so Paulw11 is right that the you probably want to inject your ObservableObject into your environment, then in each view that wants to access that instance, you just add a property with the @EnvironmentObject property wrapper. Below I've thrown together the simplest example I could think of, so that you can get the idea of how it works.

import SwiftUI
import Combine

class ManagerPlaceholder: ObservableObject {

@Published var propertyOne: Double = 1.0
@Published var propertyTwo: Double = 2.0

func action() {
propertyOne = Double.random(in: 0.0..<100.00)
propertyTwo = Double.random(in: 0.0..<100.00)
}

}

struct ContentView: View {

@EnvironmentObject var manager: ManagerPlaceholder

var body: some View {
TabView {
Subview()
.tabItem { Label("First", systemImage: "hexagon.fill") }
.tag(1)
Subview()
.tabItem { Label("Second", systemImage: "circle.fill") }
.tag(2)

}
}

}

struct Subview: View {

@EnvironmentObject var manager: ManagerPlaceholder

var body: some View {
VStack {
Text("Prop One: \(manager.propertyOne)").padding()
Text("Prop Two: \(manager.propertyTwo)").padding()
Button("Change", action: manager.action).padding()
}
}

}

So above is

  • A simple ObservableObject - All it does is set two Doubles with a random value (notice the properties you want to observe are marked as @Published)
  • A tab view
  • A simple sub-view

Notice that both of the views have a @EnvironmentObject var manager: ManagerPlaceholder. You don't set that property directly. That line says that you want to reference a ManagerPlaceholder instance that's in the Environment. The environment is a sort of "pool" of storage that SwiftUI manages for you. You can add an instance of an object to it, then reference it in sub-views that need it.

So to make sure that's in the environment you add it when instantiating a view (could be the tab view, or any super-view). So for example in your _NAME_App.swift (for an iOS 14 target) or SceneDelegate.swift (for an iOS 13 target), you'd instantiate your view like this:

ContentView().environmentObject(ManagerPlaceholder())

If you run the code above you'll see when you hit the Change button it randomly sets the two properties, and both subviews will see the exact same values when you switch back and forth, because they're both referencing the same instance.

Feel free to comment if anything is unclear.



Related Topics



Leave a reply



Submit