2017-07-12 32 views
7

tôi thấy khá thú vị những cách khác nhau để khai báo một biến trong Swift:Khai báo đúng biến trong Swift như thế nào?

// METHOD 1 
var dogName: String = "Charlie" 

// METHOD 2 
var dogName: String { 
    return "Charlie" 
} 

// METHOD 3 
let dogName = { 
    return "Charlie" 
} 

// METHOD 4 
var dogName: String = { 
    return "Charlie" 
}() 

Rõ ràng là phương pháp 3 khai báo một let và chúng tôi biết sự khác biệt; nhưng tại sao Swift lại cho phép phương pháp 4?

Sự khác nhau giữa bốn phương pháp này là gì?

Tôi khá bối rối đặc biệt giữa phương pháp 2 và 4. Ngoài ra, tại sao phương pháp 3 mất dấu ngoặc cuối cùng so với phương pháp 4?

Trả lời

5

Phương pháp 1 là khai báo biến chuẩn cho một Chuỗi.Nó có một setter và getter

var dogName: String = "Charlie" 

print(dogName) -> "Charlie" 
dogName = "Rex" // Valid 

Cách 2 là một tài sản tính của kiểu String và read-only

var dogName: String { 
    return "Charlie" 
} 

print(dogName) -> "Charlie" 
dogName = "Rex" // Invalid as property is read-only 

Phương pháp 3 là một chỉ đọc loại tài sản của() -> String , về cơ bản, một hàm lambda.

let dogName = { 
    return "Charlie" 
} 

print(dogName) -> "(Function)" 
print(dogName()) -> "Charlie" 
dogName = "Rex" // Invalid as property is read-only 

Phương pháp 4 là đóng cửa sẽ được thực hiện khi đối tượng chứa được khởi tạo. Vì nó là một var bạn có thể thay thế nó với giá trị khác

var dogName: String = { 
    return "Charlie" 
}() 

print(dogName) -> "Charlie" 
dogName = "Rex" // Valid 

Điều đó đang được nói, như Phương pháp 4 là một kết thúc, bạn có thể thực hiện các lệnh khác trong đó. Dưới đây là một ví dụ trong đó bạn có thể sử dụng cấu trúc này để khởi tạo một UILabel:

var dogNameLabel: UILabel = { 
    let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10)) 
    label.text = "Charlie" 
    return label 
}() 
+1

Bạn không hoàn toàn chính xác về phương pháp 4. Việc đóng cửa sẽ được thực thi lúc khởi tạo đối tượng, nơi mà thuộc tính được khai báo chứ không chỉ khi được truy cập. Do đó, nó giống như một giá trị thuộc tính mặc định được gán từ kết quả của việc đóng. Nhưng bạn nói đúng, sau đó bạn có thể thay thế nó bằng bất cứ thứ gì khác. –

+1

@OlegDanu Bạn hoàn toàn đúng, tôi đã cập nhật câu trả lời cho phù hợp. Cảm ơn! – Thomas

+1

Tính chất được tính toán không phải lúc nào cũng chỉ đọc, BTW. Bạn có thể định nghĩa 'get' và' set' trên các thuộc tính được tính toán để làm cho chúng đọc-ghi. Tuy nhiên, khi được khai báo như đã trình bày ở trên, chúng ngầm chỉ có một getter, điều này có hiệu quả làm cho chúng chỉ đọc. Xem https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259 –

1

Phương pháp 1 khá rõ ràng.

Trong phương pháp 2, những gì bạn đã làm là bạn đã xác định một getter cho biến nhất định.

Loại dogName trong phương pháp 3 là () -> String và không String. Những gì bạn khởi tạo có một lambda được đặt tên.

Trong phương pháp 4 bạn khởi tạo biến với hàm lambda ẩn danh (chưa được đặt tên) trả về chuỗi.

Sự khác biệt giữa 3 và 4 là trong 4 bạn gọi hàm đó bằng() để bạn nhận được chuỗi và trước đó bạn không nên nó là một hàm.

2

Tôi thiết lập thử nghiệm nhanh, đổi tên mọi thứ là dogName1, dogName2, dogName3dogName4. Sau đó, tôi thêm mã để cố gắng thay đổi từng tên thành "Snoopy".

# 2 và # 3 không được xây dựng, vì trình biên dịch biết rằng cả hai đều chỉ đọc. . (# 2, dù đã được khai báo là một var, được thiết lập để luôn luôn trở về "Charlie"

Sau khi bình luận những hai dòng ra, tôi đặt hai breakpoint - về sau khi khởi tạo, một sau khi cố gắng cập nhật

. Cuối cùng tôi đã cố gắng làm một print của mỗi một

Breakpoint # 1:. # 1 và # 4 được thiết lập để "Charlie", # 2 là không có (vì nó không được khởi tạo) và # 3 xuất hiện như được khởi tạo nhưng không có giá trị (vì nó chưa được gọi. Và có, () ở cuối khởi tạo một cái gì đó trong bộ nhớ.

Điểm dừng # 2: # 1 và # 4 được cập nhật thành "Snoopy".

Kết quả của print: # 1 và # 4 là "Snoopy", # 2 là "Charlie" và # 3 là "(Hàm)".

Kết luận: Không có sự khác biệt giữa # 1 và # 4. Mỗi trang được khai báo là var và có mặc định là "Charlie". # 2, là chỉ đọc do let và sẽ luôn trả về "Charlie". # 3? Nó tạo ra một thể hiện và không xây dựng nếu bạn cố gắng thay đổi nó - nhưng tôi không biết cách sử dụng nó.

Tôi sẽ cập nhật câu trả lời này nếu có ai đó có thêm thông tin về # 3.

1

Để hiểu sự khác nhau chúng ta hãy sử dụng một ví dụ mô tả nhiều hơn

Đây là một lớp Foo

class Foo { 

    var className = "Foo" 

    var dogName1 : String { return "Charlie " + className } 

    let dogName2 = { 
     return "My name is Charlie" 
    } 

    var dogName3 : String = { 
     var string = "My" 
     string += " name" 
     string += " is" 
     string += " Charlie" 
     print(string) 
     return string 
    }() 

} 

Bây giờ chúng ta hãy tạo một thể hiện

let foo = Foo() 
  • Phương pháp 1 là lưu trữ tài sản className với setter và getter

    let name = foo.className 
    foo.className = "Bar" 
    print(foo.className) // "Bar" 
    
  • Phương pháp 2 là tài sản tính dogName1 chỉ với một getter. Nó có thể được sử dụng để tính giá trị động.

    print(foo.dogName1) // "Charlie Bar" 
    
  • Phương pháp 3 là việc đóng cửa dogName2 loại () -> String. Nó có thể được gán cho một biến và sau đó được thực hiện

    let dogName = foo.dogName2 // assigns the closure but does not return the string. 
    print(dogName()) // "My name is Charlie" 
    
  • Phương pháp 4 là biến dogName3 mà thực hiện việc đóng cửa của nó ngay lập tức lần khi một thể hiện Foo() được khởi tạo (để được chứng minh bằng dòng print)

    print(foo.dogName3) // "My name is Charlie" but does not execute the closure and print `string` again 
    
  • Thậm chí còn có một Phương pháp 5: Nếu bạn khai báo dogName3 như lazy

    lazy var dogName3 : String = { 
    

    việc đóng không được thực hiện cho đến khi biến được truy cập lần đầu tiên. Lợi thế là bạn thậm chí có thể sử dụng self trong phần đóng không thể thực hiện được trong Cách 4.

Các vấn đề liên quan