Why It Is Called the Memberwise Initialiser

Why it is called the Memberwise Initialiser

Regarding question 1:

There is an irrevocable law in Swift:

Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.

We are talking about structs:

When creating a struct you can use the default initializer (the pair of parentheses) if all properties have a default value.

If you just declare the properties without a default value, the compiler creates an implicit memberwise initializer – which you have to use – to make sure to assign a default value to each property in a very convenient way

How can I make the memberwise initialiser public, by default, for structs in Swift?

Quoting the manual:

"Default Memberwise Initializers for Structure Types
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.

As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition."

Excerpt from "The Swift Programming Language", section "Access Control".

Why doesn't Swift provide classes memberwise initializers?

Do I need to write the initialisers by myself?

Yes.

A proposal to extend memberwise initializers to classes and make them more flexible has been discussed exhaustively on the Swift Evolution mailing list in Dec 2015/Jan 2016.

Eventually, the proposal has been rejected for various reasons, with the possibility of revisiting the topic later (after Swift 3 is released).

In the rejection, Chris Lattner noted at least one reason why memberwise initialization was offered specifically for structs: because it makes it possible to write pure "bags of properties", such as Vec4 or CGRect, with minimal overhead:

2) Memberwise init sugar strongly benefits “POD” types and other “bags of property” types (e.g. “Vec4"), and many of the C struct types that Cocoa has (CGRect etc). In these cases, clients often want to initialize all of the fields explicitly and a memberwise init proposal eliminates this boilerplate. This case is what our existing feature attempts to service.

Most classes are probably more complex, and you would want more control over the memberwise initializer, which would have made the feature much more complex, too.

A SwiftUI View's default memberwise initializer VS custom initializer

Notice that the StateObject.init(wrappedValue:) initialiser takes an autoclosure.

init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType)

This @autoclosure is propagated to the auto-generated memberwise initialiser of MyView, making the expression MyViewModel() you passed to the view lazily evaluated. This is what causes SwiftUI to only create one view model for all redraws of the view.

I can't find any documentation documenting this propagation of @autoclosure, but I can confirm it happens with this code:

@propertyWrapper
struct MyStateObject {
var wrappedValue: String

init(wrappedValue: @autoclosure () -> String) {
self.wrappedValue = wrappedValue()
}
}

struct MyView {
@MyStateObject var foo: String
}

When you compile it, there is a symbol in the binary named MyView.init(foo: @autoclosure () -> Swift.String) -> MyView. See godbolt.org

On the other hand, your handwritten initialiser does not take @autoclosure, so MyViewModel() is eagerly evaluated. The lazy expression you pass into StateObject.init(wrappedValue:) now is just the parameter name viewModel, which is not a complicated thing to evaluate :)

So to recreate the same behaviour with your own handwritten initialiser, you should add @autoclosure too:

init(viewModel: @autoclosure @escaping () -> MyViewModel) {
self._viewModel = StateObject(wrappedValue: viewModel())
}

C++ memberwise initialization and constructors

The way it is defined, B is an aggregate type if A is an aggregate type.

B b {A()};

is aggregate initialization.

When you add a move constructor to B, B stops being an aggregate type. Hence, you can't use aggregate initialization to initialize an instance of B.

From the standard (emphasis mine):

8.5.1 Aggregates [dcl.init.aggr]

1 An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equalinitializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3)

swift - can I call a struct default memberwise init from my custom init method?

Add your own initializer as an extension to your struct. Extensions cannot remove existing functionalities, so it will preserve struct's default initializer.

struct OrderFill {
let price: Int
let qty: Int
let timeStamp: NSDate
}

extension OrderFill {

init(dict: [String: AnyObject]) throws {
self.init(
price: dict["price"] as! Int,
qty: dict["qty"] as! Int,
timeStamp: try parseDate(dict["ts"] as! String)
)
}
}

let o = OrderFill(someDict)


Related Topics



Leave a reply



Submit