Swift中的"self"用于什么?

48

我对Swift还不熟悉,想知道self是用来做什么的。

我在类和结构体中看到了它,但我觉得它们并不重要,甚至没有必要在我的代码中提及。它们是用来干什么的?在什么情况下需要使用它?

我已经阅读了很多关于这个问题的问答,但没有一个完全回答了我的问题,它们总是倾向于将其与Java中的this进行比较,而我对Java一无所知。


9
在Java中,“this”与JavaScript和Objective-C中的“this”和“self”是相同的。你需要熟悉这个概念。 - Thilo
8
你真正想问的是self是什么意思,还是为什么有时候(经常)可以省略它(因为它已经被暗示了)? - Thilo
10个回答

69

是的,它与Java中的this和Objective-C中的self相同,但在Swift中,只有在从闭包调用属性或方法或区分代码中的属性名称(例如初始化程序)时才需要self。因此,除非您从闭包中进行调用,否则可以安全地使用几乎所有类组件而不必使用self

“The self Property Every instance of a type has an implicit property called self, which is exactly equivalent to the instance itself. You use the self property to refer to the current instance within its own instance methods.

The increment() method in the example above could have been written like this:

func increment() {
    self.count += 1
}

In practice, you don’t need to write self in your code very often. If you don’t explicitly write self, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of count (rather than self.count) inside the three instance methods for Counter.

The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the self property to distinguish between the parameter name and the property name.

Here, self disambiguates between a method parameter called x and an instance property that is also called x:”

摘自:苹果公司《Swift编程语言(Swift 2预发布版)》


这是Ray Wenderlich在他们的教程中推荐使用Swift中的self的方式:

使用Self

为了简洁起见,避免使用self,因为Swift不需要它来访问对象的属性或调用其方法。
在初始化程序中需要区分属性名称和参数以及在闭包表达式中引用属性时,根据编译器的要求使用self
class BoardLocation {
  let row: Int, column: Int

  init(row: Int, column: Int) {
    self.row = row
    self.column = column

    let closure = {
      println(self.row)
    }
  }
}

这是GitHub关于其应用程序中self的建议:

只有在必要时才明确引用self

当访问self的属性或方法时,默认情况下将对self的引用留作隐式。
private class History {
    var events: [Event]

    func rewrite() {
        events = []
    }
}

只有在语言需要的情况下才包含显式关键字,例如在闭包中或者参数名称发生冲突时:

extension History {
    init(events: [Event]) {
        self.events = events
    }

    var whenVictorious: () -> () {
        return {
            self.rewrite()
        }
    }
}

原因:这使得在闭包中更加突出了self的捕获语义,并避免了其他地方的冗长。


3
为什么在闭包中没有发生隐藏时它被认为是必要的? - Danyal Aytekin
我认为这应该是用来保持作用域的,但我只是猜测,需要验证一下这个信息 =) - David Gomez
3
关于闭包的评论:使用self关键词是为了让代码更易读,使属性的使用显式化。在Java中,this也可以出于同样的原因以同样的方式使用。 - Dmitry Gryazin
5
尽管我非常尊重 Ray Wenderlich 团队的工作,但我认为他们并不是关于清晰代码的最佳参考。总的来说,我建议使用 "self" 来澄清代码是本地的还是非本地的,这样一眼就能看清楚。 - brainray
1
你不应该只为了清晰而编写代码,应该使用文档来代替。 - David Gomez
显示剩余2条评论

36

在创建扩展时,你也会经常使用self关键字,例如:

extension Int {
    func square() -> Int {
        return self * self
    }

    // note: when adding mutating in front of it we don't need to specify the return type
    // and instead of "return " whatever
    // we have to use "self = " whatever

    mutating func squareMe() {
        self = self * self
    }
}
let x = 3
let y = x.square()  
println(x)         // 3
printlx(y)         // 9

现在假设您想要更改变量result本身,则必须使用mutating func来使其自身发生变化。

var z = 3

println(z)  // 3

现在让我们进行变异

z.squareMe()

println(z)  // 9

// 现在让我们看一个使用字符串的例子:

extension String {
    func x(times:Int) -> String {
        var result = ""
        if times > 0 {
            for index in 1...times{
                result += self
            }
            return result
        }
        return ""
    }

    // note: when adding mutating in front of it we don't need to specify the return type
    // and instead of "return " whatever
    // we have to use "self = " whatever

    mutating func replicateMe(times:Int){
        if times > 1 {
            let myString = self
            for index in 1...times-1{
                self = self + myString
            }
        } else {
            if times != 1 {
                self = ""
            }
        }
    } 
}


var myString1 = "Abc"
let myString2 = myString1.x(2)

println(myString1)         // "Abc"
println(myString2)         // "AbcAbc"

现在让我们更改myString1

myString1.replicateMe(3)

println(myString1)         // "AbcAbcAbc"

19
在什么情况下需要使用 it? 只有在本地变量的名称遮盖了属性的名称时才必须使用它。
然而,出于风格(以及可读性)的考虑,我总是使用它:
- 我在属性名称中使用它,否则我会想知道这个变量是什么(因为它既没有在本地声明也不是传入参数)。 - 我将其用作函数(方法)调用的接收器,以区分这些方法与顶级或本地函数。

2
谢谢Matt。最近我有一种感觉,那就是“不需要”的模式比可读性和良好的风格更重要。在我看来,风格更为重要。 - brainray

12

这就是为什么我们需要self

当我们定义一个类时,例如:

class MyClass {
    func myMethod()
}

我们正在创建一个“类对象”。是的,Class也是一个对象。

然后,无论使用类创建了多少个实例,所有实例都将具有指向其Class对象的引用指针。

你可以想象由Class定义的所有实例方法都在Class对象中,而且只会有一个副本。

enter image description here

这意味着使用Class创建的所有实例共享同一方法。

现在想象一下,你是Class对象中的myMethod,因为你被所有实例共享,所以你必须有一种方法来告诉自己正在处理哪个实例。

当有人说instance1.myMethod()时,它的意思是“嗨!myMethod,请完成你的工作,instance1是你正在处理的对象”。

要引用调用者发送给你的对象,请使用self

“实际上,在你的代码中你不需要经常写self。如果你没有显式地写self,Swift假设每当你在方法中使用已知的属性或方法名称时,你都在引用当前实例的一个属性或方法。”

摘自:Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/tw/jEUH0.l


你所描述的是面向对象和对象实例的一般方法。在这里,你仍然不需要使用self,因为编译器会隐式处理它(self是指向特定类实例或对象的VMT或类似结构的指针)。 - Mike Lischke

9
在 Swift 中,保留字self类似于Java或JavaScript中的this,但并不完全相同。
正如 @Dave Gomez 正确引用的那样:

每个类型的实例都有一个隐式属性称为 self,它与实例本身完全等效。

这里有一个主要的区别,因为:
  1. 在 Swift 中,"每个实例"(至少目前)几乎是所有东西
  2. 例如,在 Java 中,您只能在实例范围内使用单词this,在 Swift 中,您可以在几乎所有地方使用它。
以下是一些示例:
//Example 1:
var x="foo"
x.self="bar".self//compiles and run

//Example 2:
print.self(x);//compiles and run

//Example 3:
func myOther(self otherSelf:Person){}
myOther(self: personObject);//compiles and run

//Example 4:
class Foo{
      var bar=""
      init(){
          self.addSome()//this would be the same in Java
      }
      func addSome(){
          //But definitely not this:
          self.self.bar.self.self="some".self.self
      }
}
//Guess what - also compiles and run...
let f=Foo()
print(f.bar)

请参阅:为什么在Swift中“self.self”可以编译和运行,以获取更多信息。

4

我在搜索类函数self时遇到了这个问题,它看起来像这样:Int.selfString.selfYourClass.self

据我所知,之前仅有 Dmitri Pavlutin 的回答触及了这个问题,他说:

当在类型方法(static func 或 class func)中访问self时,它指的是实际的类型(而不是实例)。

当以这种方式使用self时,它实际上返回 Swift 中称为 Metatype 的东西。您可以阅读Swift 类型文档页面获取更多信息。

还有一篇关于使用和理解元类型的更详细文章,名为“.self、.Type 和 .Protocol 是什么?理解 Swift 元类型”,可以在 swiftrocks.com 上找到。


3
"如何在Swift中正确使用'self'关键字"详细解释了selfself是指向其自身的实例上的属性。它用于在方法内部访问类、结构体和枚举实例。
当在类型方法中访问self,例如static funcclass func时,它引用的是实际类型而不是实例。
Swift允许在想要访问实例属性时省略self
当一个方法参数与一个实例属性同名时,您需要明确使用self.myVariable = myVariable来区分它们。注意,方法参数优先于实例属性。

2

我对编程完全不了解,虽然这些答案很好,但对于像我这样只想尽可能简单地回答问题而不涉及难词和概念的编程新手来说,这是我精简版的超级新手版本:

使用'self'是因为编码应用程序不知道如果您在函数范围内键入变量,则要使用哪个变量。这都与作用域有关,需要明确使用哪个变量,如果其他变量具有相同的名称,则需要进行区分。作用域是花括号{}内的区域。例如:

{ scope1 {scope2} }

在此示例中,您无需使用self:

class example {
    private var exampleVar = “this is the class scope variablefunc x() {
        //You don't have to use self here
        print(exampleVar)
    }
}

这里需要使用self关键字:

class example {
        private var exampleVar = “this is the class scope variablefunc x(_ exampleVar: String) {
            //It would be confusing which exampleVar is used here so you should use self
            print(exampleVar)
            print(self.exampleVar)
        }
}

还有这种情况:

class example {
    private var exampleVar = “this is the class scope variablefunc x() {
        randomMethod { _ in
            //This is not the class scope here, so we need to use self here.
            //You will be flagged during build time of this if you don't use self.
            print(self.exampleValue)                     
        }
    }
}

1

self是实例上的一个属性,指向自身。它用于在方法内部访问类、结构体和枚举实例。

当方法参数与实例属性同名时,必须显式使用self.myVariable = myVariable来区分。

请注意,方法参数优先于实例属性。

    struct Weather {
let windSpeed: Int
  let chanceOfRain: Int
  init(windSpeed: Int, chanceOfRain: Int) {
    self.windSpeed = windSpeed
    self.chanceOfRain = chanceOfRain
  }

  func isDayForWalk() -> Bool {
    let comfortableWindSpeed = 5
    let acceptableChanceOfRain = 30
    return self.windSpeed <= comfortableWindSpeed
      && self.chanceOfRain <= acceptableChanceOfRain
  }

}
// A nice day for a walk
let niceWeather = Weather(windSpeed: 4, chanceOfRain: 25)  
print(niceWeather.isDayForWalk()) // => true

0

我有一个在Swift中使用self的优雅案例。我在像这样的块中使用它:

class MyBase {
   private var baseValue: Int = 100
   var block: ((Int)->())? = nil
   func baseMethod(anotherValue: Int) {
      guard let b = block else { return }
      b(baseValue + anotherValue)
   }
   ...
}

class MyClass {
   init()
   {
     // Define base class block. 
     // This may hold self in block causing memory leaks
     // if self is a strong reference
     block = {
       // indicate self is a weak reference 
       [weak self] (para) in
       // guaranty self existence
       guard let this = self else {return}

       let value = this.value1 + para
       // this call passes value to other obj's method
       this.obj.method(value) 
       return
     }
   }
   func callBaseBlock(otherValue: Int) {
     baseMethod(otherValue)
   }
   private var value1: Int = 1 // ini value
   private var obj: OtherClass // another class with method defined
   ...
   ...

}

这样,我保证在块中不会有对self的强引用。语法非常清晰。我使用这种方式来防止内存泄漏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接