Why cannot we use instance members in a closure definition? - swift

Problem is the following compiler error:
"Instance member 'name' cannot be used on type 'Person'"
class Person {
var name: String = "Amir"
var myClosure = { (family: String) -> Void in
print(name + " " + family)
}
func myFunc(family: String) -> Void {
print(name + " " + family)
}
}
The code inside myFunc and myClosure are the same, but I have compiler error on myClosure definition.
As I know, basically closures and functions are the same. So what difference between them make the above limitation for closures?

The problem is that I'm trying to initialize a variable using an instance's variable while the instance itself has not been created yet.
So the problem is not related to closure nature. The same problem does exist in the following code where I'm trying to initialize nickname with name instance property.
class Person {
var name: String = "Amirreza"
var nickname: String = name
}
As compiler says, "property initializers run before 'self' is available", so in the above code the self.name is not available to initialize nickname and this is the source of the problem.
Here the solution is to use "lazy" keyword before nickname definition. The lazy keywork defers the initialization of the nickname property to when it is accessed for the first time. Means when we are sure that the instance has been created and name property is available.
To read more about lazy refer to https://docs.swift.org/swift-book/LanguageGuide/Properties.html
So the correct form of above code is:
class Person {
    
var name: String = "Amirreza"
lazy var nickname: String = name
}
And the correct form of the my main question code would be:
class Person {
var name: String = "Amirreza"
lazy var myClosure = { [weak self] (family: String) -> Void in
print(self!.name + " " + family)
}
func myFunc(family: String) -> Void {
print(self.name + " " + family)
}
}
Just note that we have to refer to properties inside closures with self explicitly like self.name. Meanwhile to avoid strong reference cycles we need to define a capture list in closure definition which in my example is [weak self].
By defining self as 'weak', self turns to an optional variable, so we have to unwrap it somehow which makes me to write self!.name in the closure.

Related

Swift/SwiftUI property initializer error in struct

<I realize similar questions have already been asked, but they have very complex questions or insufficient answers (I'm a beginner with swift)>
I've included a very basic example that summarizes my issue
struct Greeting {
var name = "Bob"
var message = "Hi, " + name
}
var a = Test("John")
print(a.message)
I get the following error:
error: cannot use instance member 'name' within property initializer; property initializers run before 'self' is available
I've tried initializing the values, creating my best guess at lazy vars, and making the vars computed values. Any help would be appreciated!
You are using name before the struct is initialized. If you make message a computed properties it should work.
struct Greeting {
var name = "Bob"
var message: String {
"Hi, " + name
}
}
You are getting the error because in Swift you need to give variables some value before you can use them.
struct Greeting {
var name = "Bob" // <-- this variable is initialized with "Bob"
var message: String // <-- this var is not, you need to initialize it yourself
// which means you cannot use this before you give it a value
// a very common approach is to have your own initializer function init.
init(name: String, message: String) {
self.name = name // <-- this is initialized (again in our case)
self.message = "Hi " + name // this is now initialized the way you want it
}
// you can have many customized init, like this
init() {
// here because name is already initialized with Bob, it's ok
self.message = "Hi " + name
}
// if you don't have a init function, swift creates a basic one (or more) for you
// but it will not be the special one you wanted, like above.
}
So when Greeting is created the init function is called first. That is the place where you can customize the creation of Greeting. Before that you cannot use the variable "name" as you do in "var message = "Hi, " + name".
You can use it like this:
let greet = Greeting()
print(greet.message)

Swift: Why the non-static methods can't call static variables and constant (static let) without dynamicType?

It has destroyed my view about static variables and constant after using swift.
Why swift doesn't allow us to call static variables and constant in other methods?
for example:
class Aa {
static let name = "Aario"
func echo() {
print(name) // Error!
}
}
Mr. ogres told me to use dynamicType .
class Aa {
static var name = "Aario"
func echo() {
print(self.dynamicType.name)
}
}
let a = Aa()
a.dynamicType.name = "Aario Ai"
a.echo() // it works!!!
It works! So why should I use dynamicType to call static variables?
Finally, the answer is:
class Aa {
static var name = "Static Variable"
var name = "Member Variable"
func echo() {
print(self.dynamicType.name) // Static Variable
print(Aa.name) // Static Variable
print(name) // Member Variable
}
}
let a = Aa()
a.dynamicType.name = "Aario Ai"
Aa.name = "Static: Aario"
a.name = "Member: Aario"
a.echo() // it works!!!
It's really different with other languages.
Static variables have to be addressed with their type, even when you're writing code within this type. See The Swift Programming Language (Swift 2.2) - Properties (in "Querying and Setting Type Properties"):
Type properties are queried and set with dot syntax, just like instance properties. However, type properties are queried and set on the type, not on an instance of that type.
In your code, simply write Aa.name instead of name and you'll be fine.

Swift: How to add a class method in 'String" extension

I want to add a class function into extension:
extension String {
class func test () {
}
}
I get the error: Class methods are only allowed within classes; use 'static' to declare a static method
Or how should i call " String.test()"
But for NSString
extension NSString {
class func aaa () {
}
}
no errors.
If i add static keyword:
extension String {
static func aaa () {
self.stringByAppendingString("Hello")
}
}
Got: Expression resolves to an unused function,
So how should i add a class function also want to use self. method.
EDIT: This works!
extension String {
static func aaa (path:String) -> String {
return path.stringByAppendingString("Hello")
}
}
but about #lan's answer:
mutating func bbb(path: String) {
self += "world"
}
When i type it appears like this:
String.bbb(&<#String#>)
String.bbb(&"nihao")
Cannot invoke 'bbb' with an argument list of type '(String)'
Class and static functions are not called on an instance of a class/struct, but on the class/struct itself, so you can't just append a string to a class.
Apple Documentation:
Within the body of a type method, the implicit self property refers to
the type itself, rather than an instance of that type.
You can, however, append a string to a variable instance of a String using the mutating keyword:
extension String {
mutating func aaa() {
self += "hello"
}
}
let foo = "a"
foo.aaa() // ERROR: Immutable value of type 'String' only has mutating members named 'aaa'
var bar = "b"
bar.aaa() // "bhello"
If you are trying to use a pointer to a string as a parameter, you can use the inout keyword to alter the inputed string:
extension String {
static func aaa(inout path: String) {
path += "Hello"
}
}
var foo = "someText"
String.aaa(&foo)
foo //someTextHello
While correct, it's somewhat atypical to see a mutating member added to a String extension as shown in Ian's answer. Strings (and value types in general) are meant to be immutable so the only way to use a mutating method is to declare instances var at the call site. Most of the time in your code you should be using let constants.
As such, it is much more common to extend structs to return new instances. So this is typical:
extension String {
func appending(_ string: String) -> String {
return self + string
}
}
and then at the call site:
let hello = "Hello, "
let helloWorld = hello.appending("World!")
You'll note of course that I'm not using static at all. That's because appending(_:) needs to use the current instance value of the String we're appending to, and class/static do not refer to instances and therefore do not have values.
"Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type."
Thus when you extend a type by adding a type method you can only call other type methods through self. If you want to call an instance method you need to create an instance and call a method on that.

What is the difference between property closure and a method in Swift?

class Bartek {
var name: String = "Bartek"
var description: () -> String = {
return "Person name is" + self.name
}
func description() -> String {
return "Person name is" + name
}
}
Now I can use it like that:
var bartek = Bartek()
bartek.description()
Actually which on do i use? What is the better? a property closure or a method? When to use them?
Property closure can be optional and assignable. Example: var description: (() -> String)?
Property closure can capture variables (like self in your example). You have created a memory leak there.
If you implement protocols that defines a method, you must conform with method, not property closure.
Use property closure for strategy patterns where user of your object can change the behaviour. Use method in pretty much any other case.
Use a property if it's some (more or less) static information. Use a function to indicate that something is computed.

How to initialize let property of closure type to pointer to some method?

Have a look on following code
class Example {
let action: String -> ()
init() {
action = method //error: Variable self.action used before initialized
}
func method(s: String) {
println(s)
}
}
I am setting property of closure type to a class method. To reference class method I need to have the single properties initialized but to have it properly inicialized I need to reference that method. How do I get out of the cycle?
I know I can do something like
init() {
action = {_ in }
action = method //error: Variable self.action used before initialized
}
but that just is not nice.
The actual thing I need to do is more complex and makes much more sense bt this is the essence.
You can declare the property as implicitly unwrapped optional:
let action: (String -> ())!
That's one of the few cases when implicitly unwrapped are useful and can be safely used.
Use a lazy var as such:
The first time you access action the expression self.method will be evaluated (and by that time self is valid).
If you are uncomfortable with var action being settable, you can use (a common Apple pattern) of:
lazy private var _action : (String) -> () = self.method
var action { return _action } // only the 'getter' thus `var` is actually `let`
and if you are uncomfortable with even these two, you can use private(set) to ensure that no setter is visible. And thus,
class Example {
lazy private(set) var action: (String -()) = self.method
func method (s:String) { println (s) }
}