Swift - difference between class level instantiation and method level instantiation - swift

What is the difference between the following usages? Is there a difference?
class B { }
// usage 1
class A {
var b: B = B();
}
// usage 2
class A {
var b: B!
init() {
self.b = B()
}
}
Edit:
Some of the answers point out that in usage 2 the value does not need to be an optional since it gets a value in the initializer.

Instantiation is done in the declarative order of the assignation statements. But class level statements (stored properties) are done before method level statements:
// in this example, the order will be C, D, B, A
class MyClass {
init() {
b = B()
a = A()
}
var a: A
var b: B
var c: C = C()
var d: D = D()
}

Assuming the extra ! in usage 2 is not something you meant, no there is absolutely no difference between
// usage 1
class A {
var b: B = B();
}
and
// usage 2
class A {
var b: B
init() {
self.b = B()
}
}
It's exactly the same.

Yes, there's a huge difference between these two. In usage 2, b is an implicitly unwrapped optional. When you do:
let a = A()
then a.b will be set in both cases, but in usage 2, somebody can then do:
a.b = nil
and then you'll get an error if you try to use it.

Related

2 instances of 1 class don't share their property

I have 2 classes(A,B) that have instances of another class(C) as their property. Class C have property with some values(no matter what type). A class have methods that removes and append objects from/to C. B class have method that returns objects from class C. Now classes A and B have their views, on A view user able to append and remove object from C and view updates everytime when C changes, and view B call B class method when appear to load object from C, but method returns 0 objects.Here is code example:
class C{
var property = [Something]
}
class A{
var c = C()
func appendOrremove(){
//... some action
}
}
class B{
var c = C()
func getProperty()->[Something]{
//... return property of c
}
}
I can provide exact my code where I faced the problem, but it will much more code than above.Thanks in advance!
Okey I better show my code
class FavoriteBeers{
var favoriteBeers = [Beer]()
}
class BeersListInteractor:BeersListInteractorProtocol{
private var favoriteBeers = FavoriteBeers()
func addToFavorites(beer: Beer){
favoriteBeers.favoriteBeers.append(beer)
saveFavoriteBeers()
}
func removeFromFavorites(_ beer: Beer) {
favoriteBeers.favoriteBeers.removeAll { $0 == beer }
saveFavoriteBeers()
}
}
class FavoriteBeersInteractor:FavoriteBeersInteractorProtocol{
private var favoriteBeers = FavoriteBeers()
func getFavoriteBeers()-> [Beer]{
return favoriteBeers.favoriteBeers
}
}
You don't show how objects of the A and B classes are related but that will be important for how they share the c object. Here's a playground example that would work if something else controls A and B instances.
class Something{
}
class C{
var property = [Something()]
}
class A{
var c: C?
func appendOrremove(){
}
}
class B{
var c: C?
func getProperty()->[Something]{
return c?.property ?? []
}
}
let exC = C()
let exA = A()
exA.c = exC
let exB = B()
exB.c = exC
Another common situation would have A objects create B objects. In that case you might want classes defined something like:
class A{
var c = C()
var b: B
init() {
b = B(c)
}
func appendOrremove(){
}
}
class B{
var c: C
init(_ inC: C) {
c = inC
}
func getProperty()->[Something]{
return c.property
}
}

How can i pass class as a parameter to a function in Swift?

Let us consider i have two different classes.
class A {
var something = "Hello"
}
class B {
var something = "World"
}
Now
class C {
func request() {
//Call with class A or B it can contain any class. I can call either class A or B depending on condition
update(myClass: A or B)
}
func update(myClass:A or B ) {
print(myClass.something) //Since both class have same varaible var something so this code should work either i pass class A or B through function
}
}
Plz help me achieve this using Swift
You cannot declare a function in Swift that could accept an input argument of several different types, so you cannot declare a type as A or B. However, you don't actually need this to solve your specific problem.
Since you want to access a common property of the two class instances, you should declare that property in a protocol, make both classes conform to that protocol, then make the function take an input argument of the protocol type.
protocol SomethingProtocol {
var something: String { get }
}
class A: SomethingProtocol {
let something = "Hello"
}
class B: SomethingProtocol {
let something = "World"
}
class C {
func request() {
//Call with class A or B it can contain any class. I can call either class A or B depending on condition
update(something: A())
update(something: B())
}
func update(something: SomethingProtocol) {
print(something.something) //Since both class have same varaible var something so this code should work either i pass class A or B through function
}
}
Use a protocol
protocol MyProtocol: class {
var something: String { get set }
}
class A: MyProtocol {
var something = "Hello"
}
class B: MyProtocol {
var something = "world"
}
class C {
func update(myClass:MyProtocol ) {
print(myClass.something) //Since both class have same varaible var something so this code should work either i pass class A or B through function
}
}
usage:
let a = A()
let b = B()
let c = C()
print(c.update(myClass: a))
print(c.update(myClass: b))
Output:
hello
world
Create a protocol that both A and B conforms to and use it as the parameter type in update()
protocol SomeProtocol {
var something: String {get set}
}
func update(_ o: SomeProtocol) {
print(o.something)
}
Let it be known that I think using a protocol is the cleanest option that will best solve your problem.
However, it is possible to use Any to pass any object as a parameter, this will require checking which class you are dealing with inside your update method.
Something like this...
class C {
func update(myClass: Any) {
if let a = myClass as? A {
print(a.something)
}
if let b = myClass as? B {
print(b.something)
}
}
}
This might be neater as a switch - ref
class C {
func update(myClass: Any) {
switch myClass {
case let a as A:
print(a.something)
case let b as B:
print(b.something)
default:
print("not a thing")
}
}
}

Accessing subclass members using KeyPath

I was trying to access subclass members using KeyPath when I encountered something strange. Look at these 2 simple classes:
class A {
var a:String {
get {
return "str"
}
}
}
class B: A {
override var a:String {
get {
return "str1"
}
}
var c = 10
}
Now if I have the following code:
var m: A = B()
var k = \B.a
print(m[keyPath: k])
I will get a runtime error. It seems that you cannot access to subclass members using a WriteableKeyPath. But the following code works:
var m: A = B()
var k: AnyKeyPath = \B.a
print(m[keyPath: k]!)
I can use this code to access both \B.a and \B.c. Any idea why it is like this?
Since returned object by using AnyKeyPath is immutable, I will not be able to use it for updating object. I wonder if there is any workaround for updating subclass members using a KeyPath on parent class or not.
Update:
In order to elaborate more, here is another example:
var v: UIView = UILabel()
var k = \UILabel.text
v[keyPath: k] = "test" // Error cannot do this
I think this can be very useful.

Objects with common property

I have three different classes A, B and C with some commons properties. For instance all of them have property called index. Also all of them inheritance from other class X which doesn't have property index and I can't modify these classes (it's from framework). Now I want to create instantiation one of these classes and set index property. How I'm doing it now:
func returnPreparedObjet() -> X? {
if some_condition1 {
let a = A()
a.index = 1
return a
}
else if some_condition2 {
let b = B()
b.index = 1
return b
}
else if some_condition3 {
let c = C()
c.index = 1
return c
}
return nil
}
What is the best option to achieve it without repeat .index = 1 and return lines? Can I achieve this also in situation when A,B,C don't inheritance from X?
You could group the three classes under a common protocol:
protocol Indexed {
var index: Int { get set }
}
, and add conformance for the three classes
extension A: Indexed {}
extension B: Indexed {}
extension C: Indexed {}
no code will be needed in the extensions as the classes already declare a writable index property.
You can then simplify the returnPreparedObject to something like this:
func returnPreparedObject() -> X? {
let result: (X & Indexed)?
if some_condition1 {
result = A()
} else if some_condition2 {
result = B()
} else if some_condition3 {
result = C()
}
result?.index = 1
return result
}

Is it safe to pass variable created into function to closure?

Everyone knows standard situation with retain cycle.
class TestClass {
var aBlock: (() -> ())? = nil
let aConstant = 5
init() {
print("init")
aBlock = {
print(self.aConstant)
}
}
deinit {
print("deinit")
}
}
var testClass: TestClass? = TestClass()
testClass = nil
but what about situation like this:
class A {
let b: B
let c: C
init() {
b = B()
c = C()
}
func foo() {
let localC = c
b.bar {
localC.execute()
}
}
}
B, C are classes.
I copied c and passed into b.bar closure as localC. In my opinion this situation does not create retain cycle and there will be no problems with deallocation of A.
But I am not 100% sure and I want to ask some with more experience about this. Someone could explain me what happens with references? Tnx.
The way you set it up should not create any retain cycles.
localC is unnecessary. All that does is create another reference to the underlying instance of c. They are identical. Modifying localC would also modify c. They point to the same object. You would have to explicitly copy() in order for them to point to different objects.
With that in mind, this is a diagram of the references:
As you can see, there is no retain cycle being created.