Một giá trị chưa được khai thác của người Viking trong Swift là gì?


155

Tôi đang học Swift cho iOS 8 / OSX 10.10 bằng cách làm theo hướng dẫn này và thuật ngữ " giá trị chưa mở " được sử dụng nhiều lần, như trong đoạn này (trong Đối tượng và Lớp ):

Khi làm việc với các giá trị tùy chọn, bạn có thể viết? trước các hoạt động như phương thức, thuộc tính và đăng ký. Nếu giá trị trước? Nil, mọi thứ sau? được bỏ qua và giá trị của toàn bộ biểu thức là nil. Mặt khác, giá trị tùy chọn được mở ra và mọi thứ sau dấu? hành động trên giá trị chưa được bao bọc . Trong cả hai trường hợp, giá trị của toàn bộ biểu thức là một giá trị tùy chọn.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare?.sideLength

Tôi không hiểu và tìm kiếm trên web mà không gặp may.

Những gì hiện phương tiện này?


Biên tập

Từ câu trả lời của Cezary, có một chút khác biệt giữa đầu ra của mã gốc và giải pháp cuối cùng (được thử nghiệm trên sân chơi):

Mã gốc

Mã gốc

Giải pháp của Cezary

Giải pháp của Cezary

Các thuộc tính của siêu lớp được hiển thị trong đầu ra trong trường hợp thứ hai, trong khi có một đối tượng trống trong trường hợp đầu tiên.

Không phải là kết quả được cho là giống hệt nhau trong cả hai trường hợp?

Câu hỏi liên quan: Giá trị tùy chọn trong Swift là gì?


1
Câu trả lời của Cezary là tại chỗ. Ngoài ra, có một cuốn sách giới thiệu tuyệt vời về Swift có sẵn MIỄN PHÍ trên iBooks
Daniel Storm

@DanielStormApps iBook này là phiên bản di động của hướng dẫn được liên kết trong câu hỏi của tôi :)
Bigood

Câu trả lời:


289

Đầu tiên, bạn phải hiểu loại Tùy chọn là gì. Một loại tùy chọn về cơ bản có nghĩa là biến có thể nil .

Thí dụ:

var canBeNil : Int? = 4
canBeNil = nil

Dấu hỏi chỉ ra thực tế canBeNilcó thể nil.

Điều này sẽ không hoạt động:

var cantBeNil : Int = 4
cantBeNil = nil // can't do this

Để có được giá trị từ biến của bạn nếu nó là tùy chọn, bạn phải mở khóa . Điều này chỉ có nghĩa là đặt một dấu chấm than ở cuối.

var canBeNil : Int? = 4
println(canBeNil!)

Mã của bạn sẽ trông như thế này:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare!.sideLength

Một sidenote:

Bạn cũng có thể khai báo các tùy chọn để tự động mở khóa bằng cách sử dụng dấu chấm than thay vì dấu chấm hỏi.

Thí dụ:

var canBeNil : Int! = 4
print(canBeNil) // no unwrapping needed

Vì vậy, một cách khác để sửa mã của bạn là:

let optionalSquare: Square! = Square(sideLength: 2.5, name: "optional square") 
let sideLength = optionalSquare.sideLength

BIÊN TẬP:

Sự khác biệt mà bạn đang thấy chính xác là triệu chứng của thực tế là giá trị tùy chọn được gói lại . Có một lớp khác trên đầu trang của nó. Các quà nào phiên bản chỉ cho thấy các đối tượng thẳng vì nó là, tốt, quà nào.

Một so sánh sân chơi nhanh:

sân chơi

Trong trường hợp thứ nhất và thứ hai, đối tượng không được tự động mở khóa, vì vậy bạn thấy hai "lớp" ( {{...}}), trong khi đó trong trường hợp thứ ba, bạn chỉ thấy một lớp ( {...}) vì đối tượng đang được tự động mở khóa.

Sự khác biệt giữa trường hợp thứ nhất và hai trường hợp thứ hai là hai trường hợp thứ hai sẽ cung cấp cho bạn lỗi thời gian chạy nếu optionalSquaređược đặt thành nil. Sử dụng cú pháp trong trường hợp đầu tiên, bạn có thể làm một cái gì đó như thế này:

if let sideLength = optionalSquare?.sideLength {
    println("sideLength is not nil")
} else {
    println("sidelength is nil")
}

1
Cảm ơn đã giải thích rõ ràng! Mã bao gồm trong câu hỏi của tôi được trích dẫn từ hướng dẫn của Apple (liên kết trong câu hỏi) và tôi đoán nó được coi là hoạt động như hiện tại (và nó cũng vậy). Mặc dù, giải pháp cuối cùng của bạn và giải pháp ban đầu cho kết quả khác nhau (xem phần chỉnh sửa của tôi). Bất cứ suy nghĩ nào?
Bigood

2
Mát mẻ. Giải thích tuyệt vời. Tôi vẫn còn bối rối. Tại sao tôi muốn sử dụng một giá trị tùy chọn? Khi nào nó hữu ích? Tại sao Apple tạo ra hành vi và cú pháp này?
Todd Lehman

1
Nó đặc biệt có liên quan khi nói đến thuộc tính lớp. Swift yêu cầu tất cả các phương thức không tùy chọn được khởi tạo trong init()chức năng của lớp. Tôi khuyên bạn nên đọc các chương "Cơ bản" (bao gồm các tùy chọn), "Khởi tạo" và "Chuỗi tùy chọn" trong cuốn sách Swift do Apple phát hành.
Cezary Wojcik

2
@ToddLehman Apple đã tạo ra điều này để giúp bạn viết mã an toàn hơn nhiều. Ví dụ, hãy tưởng tượng tất cả các ngoại lệ con trỏ null trong Java làm hỏng chương trình vì ai đó quên kiểm tra null. Hoặc hành vi kỳ lạ trên Objective-C vì bạn quên kiểm tra con số không. Với loại Tùy chọn, bạn không thể quên đối phó với con số không. Trình biên dịch buộc bạn phải đối phó với nó.
Erik Engheim

5
@AdamSmith - Đến từ nền Java, tôi chắc chắn có thể thấy việc sử dụng các tùy chọn ... nhưng cũng đến (gần đây hơn) từ nền Objective-C, tôi vẫn gặp khó khăn khi xem việc sử dụng nó, bởi vì Objective- C rõ ràng cho phép bạn gửi tin nhắn đến các đối tượng không. Hừm. Tôi rất thích thấy ai đó viết ra một ví dụ về những điều này cho thấy cách họ giúp làm cho mã ngắn hơn và an toàn hơn mã tương đương không sử dụng chúng. Tôi không nói rằng chúng không hữu ích; Tôi chỉ nói rằng tôi chưa "hiểu được".
Todd Lehman

17

Câu trả lời đúng hiện tại là rất tốt, nhưng tôi thấy rằng để tôi hiểu điều này một cách đầy đủ, tôi cần một sự tương tự tốt, vì đây là một khái niệm rất trừu tượng và kỳ lạ.

Vì vậy, hãy để tôi giúp những nhà phát triển "não phải" (tư duy trực quan) đó bằng cách đưa ra một quan điểm khác bên cạnh câu trả lời đúng. Đây là một sự tương tự tốt đã giúp tôi rất nhiều.

Món quà sinh nhật tương tự

Hãy nghĩ về các tùy chọn giống như những món quà sinh nhật đi kèm trong gói cứng, cứng, màu.

Bạn không biết nếu có bất cứ điều gì bên trong gói cho đến khi bạn mở gói quà - có lẽ không có gì ở bên trong! Nếu có một cái gì đó bên trong, nó có thể là một món quà khác, cũng được gói và cũng không chứa gì . Bạn thậm chí có thể mở ra 100 món quà lồng nhau để cuối cùng phát hiện ra không có gì ngoài gói .

Nếu giá trị của tùy chọn là không nil, thì bây giờ bạn đã tiết lộ một hộp chứa thứ gì đó . Nhưng, đặc biệt là nếu giá trị không gõ một cách rõ ràng và là một biến và không phải là một hằng số được xác định trước, sau đó bạn có thể vẫn cần phải mở hộp trước khi bạn có thể biết bất cứ điều gì cụ thể về những gì trong hộp, giống như những gì gõ nó là, hoặc những gì giá trị thực tế là.

Có cái gì trong hộp vậy?! Sự giống nhau

Ngay cả sau khi bạn mở khóa biến, bạn vẫn giống Brad Pitt trong cảnh cuối cùng trong SE7EN ( cảnh báo : kẻ phá hoại và ngôn ngữ hôi và bạo lực rất được xếp hạng R), bởi vì ngay cả sau khi bạn đã mở khóa hiện tại, bạn vẫn ở trong tình huống sau: bây giờ bạn có nil, hoặc một hộp chứa thứ gì đó (nhưng bạn không biết cái gì).

Bạn có thể biết loại của một cái gì đó . Ví dụ: nếu bạn đã khai báo biến là loại, [Int:Any?]thì bạn sẽ biết bạn có Từ điển (có khả năng trống) với các chỉ số nguyên mang lại nội dung được bọc trong bất kỳ loại cũ nào.

Đó là lý do tại sao việc xử lý các loại bộ sưu tập (Từ điển và Mảng) trong Swift có thể có loại lông.

Trường hợp tại điểm:

typealias presentType = [Int:Any?]

func wrap(i:Int, gift:Any?) -> presentType? {
    if(i != 0) {
        let box : presentType = [i:wrap(i-1,gift:gift)]
        return box
    }
    else {
        let box = [i:gift]
        return box
    }
}

func getGift() -> String? {
    return "foobar"
}

let f00 = wrap(10,gift:getGift())

//Now we have to unwrap f00, unwrap its entry, then force cast it into the type we hope it is, and then repeat this in nested fashion until we get to the final value.

var b4r = (((((((((((f00![10]! as! [Int:Any?])[9]! as! [Int:Any?])[8]! as! [Int:Any?])[7]! as! [Int:Any?])[6]! as! [Int:Any?])[5]! as! [Int:Any?])[4]! as! [Int:Any?])[3]! as! [Int:Any?])[2]! as! [Int:Any?])[1]! as! [Int:Any?])[0])

//Now we have to DOUBLE UNWRAP the final value (because, remember, getGift returns an optional) AND force cast it to the type we hope it is

let asdf : String = b4r!! as! String

print(asdf)

tốt đẹp 😊 😊😊 😊 😊😊
SamSol

tình yêu tương tự! Vì vậy, có cách nào tốt hơn để mở hộp sau đó? trong ví dụ mã của bạn
David T.

Con mèo của Schrödinger ở trong hộp
Jacksonkr

Cảm ơn vì đã làm tôi bối rối .... Kiểu lập trình viên nào tặng quà sinh nhật dù sao cũng trống rỗng? có nghĩa là
delta2flat

@Jacksonkr Hoặc không.
amsmath

13

Swift đặt một phí bảo hiểm cao về loại an toàn. Toàn bộ ngôn ngữ Swift được thiết kế với tâm trí an toàn. Đó là một trong những đặc điểm nổi bật của Swift và là một trong những điều bạn nên chào đón với vòng tay rộng mở. Nó sẽ hỗ trợ phát triển mã sạch, dễ đọc và giúp ứng dụng của bạn không bị sập.

Tất cả các tùy chọn trong Swift được phân định với ?biểu tượng. Bằng cách đặt tên ?sau của loại mà bạn đang khai báo là tùy chọn, về cơ bản bạn sẽ chọn loại này không phải là loại nằm trước ?, mà thay vào đó là loại tùy chọn .


Lưu ý: Một biến hoặc loại Intkhông giống như Int?. Chúng là hai loại khác nhau không thể được vận hành lẫn nhau.


Sử dụng tùy chọn

var myString: String?

myString = "foobar"

Điều này không có nghĩa là bạn đang làm việc với một loại String. Điều này có nghĩa là bạn đang làm việc với một loại String?(Chuỗi tùy chọn hoặc Chuỗi tùy chọn). Trong thực tế, bất cứ khi nào bạn cố gắng để

print(myString)

tại thời gian chạy, bàn điều khiển gỡ lỗi sẽ in Optional("foobar"). Phần " Optional()" chỉ ra rằng biến này có thể có hoặc không có giá trị khi chạy, nhưng thực tế là hiện tại có chứa chuỗi "foobar". "Đây Optional()" dấu hiệu sẽ vẫn trừ khi bạn làm những gì được gọi là "unwrapping" giá trị bắt buộc.

Unwrapping một tùy chọn có nghĩa là bạn hiện đang chọn loại đó là không tùy chọn. Điều này sẽ tạo ra một loại mới và gán giá trị nằm trong tùy chọn đó cho loại không tùy chọn mới. Bằng cách này, bạn có thể thực hiện các thao tác trên biến đó vì nó đã được trình biên dịch đảm bảo có giá trị vững chắc.


Unwrapping có điều kiện sẽ kiểm tra xem giá trị trong tùy chọn có nilhay không. Nếu nó không phải là nil, sẽ có một biến cố mới được tạo ra sẽ được gán giá trị và quà nào vào hằng số không bắt buộc. Và từ đó bạn có thể sử dụng một cách không an toàn trong ifkhối.

Lưu ý: Bạn có thể đặt tên không đổi có điều kiện cùng tên với biến tùy chọn mà bạn đang hủy ghép.

if let myString = myString {
    print(myString)
    // will print "foobar"
}

Tùy chọn hủy ghép có điều kiện là cách sạch nhất để truy cập giá trị của tùy chọn vì nếu nó chứa giá trị không thì mọi thứ trong khối if let sẽ không thực thi. Tất nhiên, giống như bất kỳ câu lệnh if nào, bạn có thể bao gồm một khối khác

if let myString = myString {
    print(myString)
    // will print "foobar"
}
else {
    print("No value")
}

Buộc Unwrapping được thực hiện bằng cách sử dụng toán tử được gọi là toán tử !("bang"). Điều này ít an toàn hơn nhưng vẫn cho phép mã của bạn biên dịch. Tuy nhiên, bất cứ khi nào bạn sử dụng toán tử bang, bạn phải chắc chắn 1000% rằng biến của bạn thực tế có chứa một giá trị vững chắc trước khi buộc phải hủy bỏ.

var myString: String?

myString = "foobar"

print(myString!)

Đây là mã Swift hoàn toàn hợp lệ. Nó in ra giá trị của myStringcái đó được đặt là "foobar". Người dùng sẽ thấy foobarđược in trong bảng điều khiển và đó là về nó. Nhưng hãy giả sử giá trị không bao giờ được đặt:

var myString: String?

print(myString!) 

Bây giờ chúng tôi có một tình huống khác nhau trên tay của chúng tôi. Không giống như Objective-C, bất cứ khi nào một nỗ lực được thực hiện để buộc mở khóa tùy chọn và tùy chọn chưa được đặt và nilkhi bạn cố gắng mở khóa tùy chọn để xem những gì bên trong ứng dụng của bạn sẽ gặp sự cố.


Unwrapping w / Kiểu đúc . Như chúng tôi đã nói trước đó, trong khi bạn là unwrappingtùy chọn, bạn thực sự đang chuyển sang loại không tùy chọn, bạn cũng có thể chuyển từ không tùy chọn sang loại khác. Ví dụ:

var something: Any?

Ở đâu đó trong mã của chúng tôi, biến somethingsẽ được đặt với một số giá trị. Có thể chúng ta đang sử dụng thuốc generic hoặc có thể có một số logic khác đang diễn ra sẽ khiến điều này thay đổi. Vì vậy, sau này trong mã của chúng tôi, chúng tôi muốn sử dụng somethingnhưng vẫn có thể xử lý nó khác nếu nó là một loại khác. Trong trường hợp này, bạn sẽ muốn sử dụng astừ khóa để xác định điều này:

Lưu ý: asToán tử là cách bạn nhập cast trong Swift.

// Conditionally
if let thing = something as? Int {
    print(thing) // 0
}

// Optionally
let thing = something as? Int
print(thing) // Optional(0)

// Forcibly
let thing = something as! Int
print(thing) // 0, if no exception is raised

Lưu ý sự khác biệt giữa hai astừ khóa. Giống như trước đây khi chúng tôi buộc phải hủy một tùy chọn, chúng tôi đã sử dụng !toán tử bang để làm như vậy. Ở đây bạn sẽ làm điều tương tự nhưng thay vì chỉ chọn không phải là tùy chọn, bạn cũng sẽ chọn nó Int. Và nó phải có khả năng bị hạ thấp vì Intnếu không, như sử dụng toán tử bang khi giá trị là nilứng dụng của bạn sẽ bị sập.

Và để sử dụng tất cả các biến này trong một số hoạt động sắp xếp hoặc toán học, chúng phải được mở ra để làm như vậy.

Chẳng hạn, trong Swift chỉ các loại dữ liệu số hợp lệ cùng loại có thể được vận hành lẫn nhau. Khi bạn sử dụng một loại với loại as!bạn đang buộc downcast của biến đó như thể bạn chắc chắn đó là loại đó, do đó an toàn để hoạt động và không làm hỏng ứng dụng của bạn. Điều này là ổn miễn là biến thực sự thuộc loại bạn đang sử dụng, nếu không bạn sẽ có một mớ hỗn độn trên tay.

Tuy nhiên, việc truyền với as!sẽ cho phép mã của bạn biên dịch. Bằng cách đúc với một as?là một câu chuyện khác nhau. Trong thực tế, as?khai báo của bạn Intlà một loại dữ liệu hoàn toàn khác nhau cùng nhau.

Bây giờ thì đúng là vậy Optional(0)

Và nếu bạn đã từng cố gắng làm bài tập về nhà của mình bằng cách viết

1 + Optional(1) = 2

Giáo viên toán của bạn có thể đã cho bạn một "F". Tương tự với Swift. Ngoại trừ Swift thà không biên dịch gì cả hơn là cho bạn điểm. Bởi vì vào cuối ngày, tùy chọn trên thực tế có thể là con số không .

An toàn cho trẻ em đầu tiên.


Giải thích tốt đẹp của các loại tùy chọn. Đã giúp mọi thứ rõ ràng lên cho tôi.
Tobe_Sta

0

'?' có nghĩa là biểu thức chuỗi tùy chọn, nghĩa là

'!' là giá trị lực
nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.