เป็น Swift Pass By Value หรือ Pass By Reference


101

ฉันใหม่มากสำหรับ Swift และฉันเพิ่งอ่านว่าคลาสถูกส่งผ่านโดยการอ้างอิงและอาร์เรย์ / สตริง ฯลฯ ถูกคัดลอก

pass by reference เป็นแบบเดียวกับใน Objective-C หรือ Java ซึ่งคุณส่งผ่าน "a" reference หรือเปล่าหรือว่า pass by reference ถูกต้อง?


"pass by reference เหมือนกับใน Objective-C หรือ Java" ทั้ง Objective-C หรือ Java ไม่มีการอ้างอิงแบบพาส
newacct

2
ใช่. ฉันรู้แล้ว. คุณไม่ผ่านการอ้างอิง คุณส่งต่อการอ้างอิงตามค่า ฉันเดาว่านั่นคือสิ่งที่รู้เมื่อตอบ
gran_profaci

Java ส่งผ่านค่าไม่อ้างอิง
6rchid

คำตอบ:


169

ประเภทของสิ่งต่างๆใน Swift

กฎคือ:

  • อินสแตนซ์คลาสเป็นประเภทอ้างอิง (กล่าวคือการอ้างอิงของคุณไปยังอินสแตนซ์คลาสเป็นตัวชี้อย่างมีประสิทธิภาพ)

  • ฟังก์ชันคือประเภทอ้างอิง

  • ทุกสิ่งทุกอย่างเป็นประเภทค่า ; "ทุกอย่างอื่น" หมายถึงอินสแตนซ์ของโครงสร้างและอินสแตนซ์ของ enum เพราะนั่นคือทั้งหมดที่มีใน Swift อาร์เรย์และสตริงเป็นอินสแตนซ์โครงสร้างตัวอย่างเช่น คุณสามารถส่งต่อการอ้างอิงไปยังสิ่งเหล่านั้น (เป็นอาร์กิวเมนต์ของฟังก์ชัน) โดยใช้inoutและรับแอดเดรสตามที่ newacct ได้ระบุไว้ แต่ประเภทนั้นเป็นประเภทค่า

ประเภทอ้างอิงมีความหมายสำหรับคุณอย่างไร

วัตถุประเภทอ้างอิงมีความพิเศษในทางปฏิบัติเนื่องจาก:

  • การกำหนดหรือการส่งผ่านไปยังฟังก์ชันสามารถให้การอ้างอิงหลายรายการไปยังวัตถุเดียวกันได้

  • วัตถุนั้นไม่แน่นอนแม้ว่าการอ้างอิงถึงมันจะเป็นค่าคงที่letก็ตาม( ไม่ว่าจะโดยชัดแจ้งหรือโดยนัย)

  • การกลายพันธุ์ของวัตถุมีผลต่อวัตถุนั้นตามที่เห็นได้จากการอ้างอิงทั้งหมดกับวัตถุนั้น

สิ่งเหล่านี้อาจเป็นอันตรายได้ดังนั้นโปรดระวัง ในทางกลับกันการส่งผ่านประเภทการอ้างอิงมีประสิทธิภาพอย่างชัดเจนเนื่องจากมีการคัดลอกและส่งผ่านตัวชี้เท่านั้นซึ่งเป็นเรื่องเล็กน้อย

คุณค่าประเภทใดที่มีความหมายสำหรับคุณ

เห็นได้ชัดว่าการส่งผ่านประเภทค่านั้น "ปลอดภัยกว่า" และletหมายถึงสิ่งที่ระบุ: คุณไม่สามารถเปลี่ยนอินสแตนซ์โครงสร้างหรืออินสแตนซ์ enum ผ่านการletอ้างอิงได้ ในทางกลับกันความปลอดภัยนั้นเกิดขึ้นได้จากการทำสำเนาค่าแยกต่างหากใช่หรือไม่? นั่นไม่ทำให้การส่งผ่านประเภทมูลค่าอาจมีราคาแพงหรือไม่?

ใช่และไม่ใช่ มันไม่เลวร้ายอย่างที่คุณคิด ดังที่ Nate Cook ได้กล่าวไว้การส่งผ่านประเภทค่าไม่จำเป็นต้องหมายความถึงการคัดลอกเนื่องจากlet(โดยชัดแจ้งหรือโดยนัย) รับประกันความไม่เปลี่ยนรูปดังนั้นจึงไม่จำเป็นต้องคัดลอกอะไรเลย และแม้กระทั่งการส่งต่อไปยังvarการอ้างอิงไม่ได้หมายความว่าจะมีการคัดลอกสิ่งต่าง ๆแต่สามารถเป็นได้หากจำเป็น (เนื่องจากมีการกลายพันธุ์) เอกสารนี้แนะนำให้คุณไม่ต้องบิดกางเกงใน


6
"อินสแตนซ์คลาสถูกส่งต่อโดยการอ้างอิงฟังก์ชันจะถูกส่งผ่านโดยการอ้างอิง" Nope เป็น pass-by-value เมื่อพารามิเตอร์ไม่inoutคำนึงถึงประเภท ไม่ว่าบางสิ่งจะเป็นแบบพาส - บาย - อ้างอิงจะตั้งฉากกับประเภทหรือไม่
newacct

5
@newacct แน่นอนคุณถูกต้องในแง่ที่เข้มงวด! อย่างเคร่งครัดหนึ่งควรจะพูดว่าทุกอย่างจะผ่านโดยค่า แต่กรณี enum และกรณี struct มีค่าชนิดและว่ากรณีการเรียนและฟังก์ชั่นประเภทการอ้างอิง ดูตัวอย่างเช่นdeveloper.apple.com/swift/blog/?id=10 - โปรดดูที่developer.apple.com/library/ios/documentation/Swift/Conceptual/…อย่างไรก็ตามฉันคิดว่าสิ่งที่ฉันพูดสอดคล้องกับทั่วไป ความหมายของคำว่าหมายถึง
แมตต์

7
ไม่ควรสับสนระหว่างประเภทขวาและค่า / ประเภทการอ้างอิงกับ pass-by-value / pass-by-reference เนื่องจากประเภทของค่าสามารถส่งผ่านโดยค่าหรือโดยการอ้างอิงและประเภทการอ้างอิงสามารถส่งผ่านโดยค่าหรือโดยการอ้างอิงได้
newacct

1
@newacct สนทนาที่มีประโยชน์มาก; ฉันเขียนบทสรุปใหม่เพื่อมิให้เข้าใจผิด
แมต

@newacct ฉันยังคงมองว่าความคิดเห็นของคุณค่อนข้างทำให้เข้าใจผิด คนทั่วไปที่อ่านเรื่องนี้อยู่ที่นี่เพราะเขาไม่ใช่ผู้เชี่ยวชาญด้าน Swift เพียง แต่ระบุว่าทุกสิ่งถูกส่งผ่านไปด้วยมูลค่านั้นทำให้เกิดความสับสนและไม่ชัดเจนเช่นว่าสามารถส่งผ่านประเภทอ้างอิงโดยการอ้างอิงได้จึงชี้ไปที่ตัวชี้ซึ่งส่วนใหญ่น่าจะไม่ใช่เจตนาของผู้พัฒนา คำตอบเดิมของ Matt ตรงประเด็น
Adrian Bartholomew

47

ทุกอย่างใน Swift จะถูกส่งผ่านโดย "copy" ตามค่าเริ่มต้นดังนั้นเมื่อคุณส่งผ่านประเภทค่าคุณจะได้รับสำเนาของค่าและเมื่อคุณส่งผ่านประเภทการอ้างอิงคุณจะได้รับสำเนาของการอ้างอิงซึ่งหมายความว่าทั้งหมดนั้น (นั่นคือสำเนาของการอ้างอิงยังคงชี้ไปที่อินสแตนซ์เดียวกันกับการอ้างอิงดั้งเดิม)

ฉันใช้คำพูดที่ทำให้ตกใจรอบ ๆ "สำเนา" ด้านบนเพราะ Swift ทำการเพิ่มประสิทธิภาพมากมาย ทุกที่ที่เป็นไปได้จะไม่คัดลอกจนกว่าจะมีการกลายพันธุ์หรือมีความเป็นไปได้ของการกลายพันธุ์ เนื่องจากพารามิเตอร์ไม่เปลี่ยนรูปตามค่าเริ่มต้นจึงหมายความว่าส่วนใหญ่แล้วจะไม่มีการคัดลอกเกิดขึ้นจริง


สำหรับฉันนี่เป็นคำตอบที่ดีที่สุดเนื่องจากชี้แจงว่าตัวอย่างเช่นคุณสมบัติของอินสแตนซ์สามารถแก้ไขได้ภายในฟังก์ชันแม้พารามิเตอร์จะเป็นสำเนา (ส่งผ่านค่า) เนื่องจากชี้ไปที่การอ้างอิงเดียวกัน
MrAn3

คำตอบที่ดีที่สุด.
Adrian Bartholomew

45

มันเป็นเรื่องที่มักจะ ผ่านโดยค่าinoutเมื่อพารามิเตอร์ไม่

มันเป็นเรื่องที่มักจะ ผ่านโดยการอ้างอิงinoutถ้าพารามิเตอร์เป็น อย่างไรก็ตามสิ่งนี้ค่อนข้างซับซ้อนเนื่องจากคุณต้องใช้ตัว&ดำเนินการกับอาร์กิวเมนต์อย่างชัดเจนเมื่อส่งผ่านไปยังinoutพารามิเตอร์ดังนั้นจึงอาจไม่ตรงกับคำจำกัดความแบบดั้งเดิมของการอ้างอิงแบบพาสโดยอ้างอิงซึ่งคุณส่งผ่านตัวแปรโดยตรง


3
คำตอบนี้เมื่อรวมกับ Nate Cook แล้วชัดเจนสำหรับฉัน (มาจาก C ++) เกี่ยวกับข้อเท็จจริงที่ว่าแม้แต่ "ประเภทการอ้างอิง" ก็จะไม่ถูกแก้ไขนอกขอบเขตฟังก์ชันเว้นแต่คุณจะระบุไว้อย่างชัดเจน (โดยใช้inout)
Gobe

10
inoutไม่ได้ผ่านการอ้างอิงจริง ๆ แต่การคัดลอกในการคัดลอกออกมันรับประกันได้ว่าค่าที่แก้ไขหลังจากการเรียกฟังก์ชันจะถูกกำหนดให้กับอาร์กิวเมนต์ดั้งเดิมเท่านั้น พารามิเตอร์เข้า
Dalija Prasnikar

1
แม้ว่ามันจะเป็นเรื่องจริงที่ทุกอย่างผ่านไปด้วยคุณค่า คุณสมบัติชนิดการอ้างอิงสามารถแก้ไขได้ภายในฟังก์ชันเนื่องจากสำเนาอ้างอิงไปยังอินสแตนซ์เดียวกัน
MrAn3

9

นี่คือตัวอย่างโค้ดขนาดเล็กสำหรับส่งต่อโดยอ้างอิง หลีกเลี่ยงการทำเช่นนี้เว้นแต่คุณจะมีเหตุผลที่ชัดเจน

func ComputeSomeValues(_ value1: inout String, _ value2: inout Int){
    value1 = "my great computation 1";
    value2 = 123456;
}

เรียกแบบนี้

var val1: String = "";
var val2: Int = -1;
ComputeSomeValues(&val1, &val2);

1
ทำไมคุณควรหลีกเลี่ยงการทำเช่นนี้?
Brainless

1
@Brainless เพราะเพิ่มความซับซ้อนโดยไม่จำเป็นให้กับโค้ด วิธีที่ดีที่สุดคือใช้พารามิเตอร์และส่งคืนผลลัพธ์เดียว การทำเช่นนี้มักส่งสัญญาณการออกแบบที่ไม่ดี อีกวิธีหนึ่งในการระบุคือผลข้างเคียงที่ซ่อนอยู่ในตัวแปรอ้างอิงที่ส่งผ่านมานั้นไม่โปร่งใสสำหรับผู้โทร
Chris Amelinckx

สิ่งนี้ไม่ผ่านการอ้างอิง inoutเป็นตัวดำเนินการคัดลอกคัดลอกออก ขั้นแรกจะคัดลอกในวัตถุจากนั้นเขียนทับวัตถุเดิมหลังจากที่ฟังก์ชันกลับมา แม้ว่าอาจดูเหมือนเหมือนกัน แต่ก็มีความแตกต่างที่ลึกซึ้ง
Hannes Hertach

7

นักพัฒนาแอปเปิ้ลสวิฟท์บล็อกมีการโพสต์ที่เรียกว่ามูลค่าและการอ้างอิงประเภทที่ให้การสนทนาที่ชัดเจนและรายละเอียดเกี่ยวกับเรื่องนี้มาก

อ้างถึง:

ประเภทใน Swift แบ่งออกเป็นหนึ่งในสองประเภท ได้แก่ ประเภทแรก“ ประเภทค่า” โดยแต่ละอินสแตนซ์จะเก็บสำเนาข้อมูลที่ไม่ซ้ำกันโดยปกติจะกำหนดเป็นโครงสร้าง, enum หรือทูเปิล ประเภทที่สองคือ "ประเภทการอ้างอิง" โดยที่อินสแตนซ์แบ่งปันสำเนาข้อมูลเดียวและประเภทนี้มักกำหนดเป็นคลาส

บล็อกโพสต์ของ Swift ยังคงอธิบายความแตกต่างพร้อมตัวอย่างและแนะนำว่าคุณจะใช้เมื่อใด


1
สิ่งนี้ไม่ตอบคำถาม คำถามเกี่ยวกับ pass-by-value กับ pass-by-reference ซึ่งมีมุมฉากอย่างสมบูรณ์กับประเภทค่าเทียบกับประเภทอ้างอิง
Jörg W Mittag

2

ชั้นเรียนจะถูกส่งต่อโดยการอ้างอิงและอื่น ๆ จะถูกส่งต่อโดยค่าเริ่มต้น คุณสามารถผ่านการอ้างอิงโดยใช้inoutคำสำคัญ


สิ่งนี้ไม่ถูกต้อง inoutเป็นตัวดำเนินการคัดลอกคัดลอกออก ขั้นแรกจะคัดลอกในวัตถุจากนั้นเขียนทับวัตถุเดิมหลังจากที่ฟังก์ชันกลับมา แม้ว่าอาจดูเหมือนเหมือนกัน แต่ก็มีความแตกต่างที่ลึกซึ้ง
Hannes Hertach

2

เมื่อคุณใช้ inout กับตัวดำเนินการ infix เช่น + = สัญลักษณ์ & address จะถูกละเว้น ฉันเดาว่าคอมไพเลอร์ถือว่าผ่านการอ้างอิง?

extension Dictionary {
    static func += (left: inout Dictionary, right: Dictionary) {
        for (key, value) in right {
            left[key] = value
        }
    }
}

origDictionary + = newDictionaryToAdd

และอย่างดีพจนานุกรมนี้ 'เพิ่ม' เพียงหนึ่งเดียวที่เขียนไปยังการอ้างอิงต้นฉบับด้วยเช่นกันเหมาะสำหรับการล็อค!


2

ชั้นเรียนและโครงสร้าง

ความแตกต่างที่สำคัญที่สุดอย่างหนึ่งระหว่างโครงสร้างและคลาสคือโครงสร้างจะถูกคัดลอกเสมอเมื่อส่งผ่านไปในโค้ดของคุณ แต่คลาสจะถูกส่งผ่านโดยการอ้างอิง

การปิด

หากคุณกำหนดการปิดให้กับคุณสมบัติของอินสแตนซ์คลาสและการปิดจับอินสแตนซ์นั้นโดยอ้างถึงอินสแตนซ์หรือสมาชิกคุณจะสร้างวงจรอ้างอิงที่ชัดเจนระหว่างการปิดและอินสแตนซ์ Swift ใช้รายการจับภาพเพื่อทำลายวงจรอ้างอิงที่แข็งแกร่งเหล่านี้

ARC (การนับอ้างอิงอัตโนมัติ)

การนับอ้างอิงใช้กับอินสแตนซ์ของคลาสเท่านั้น โครงสร้างและการแจงนับเป็นประเภทค่าไม่ใช่ชนิดอ้างอิงและไม่ได้จัดเก็บและส่งต่อโดยการอ้างอิง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.