ฉันใหม่มากสำหรับ Swift และฉันเพิ่งอ่านว่าคลาสถูกส่งผ่านโดยการอ้างอิงและอาร์เรย์ / สตริง ฯลฯ ถูกคัดลอก
pass by reference เป็นแบบเดียวกับใน Objective-C หรือ Java ซึ่งคุณส่งผ่าน "a" reference หรือเปล่าหรือว่า pass by reference ถูกต้อง?
ฉันใหม่มากสำหรับ Swift และฉันเพิ่งอ่านว่าคลาสถูกส่งผ่านโดยการอ้างอิงและอาร์เรย์ / สตริง ฯลฯ ถูกคัดลอก
pass by reference เป็นแบบเดียวกับใน Objective-C หรือ Java ซึ่งคุณส่งผ่าน "a" reference หรือเปล่าหรือว่า pass by reference ถูกต้อง?
คำตอบ:
กฎคือ:
อินสแตนซ์คลาสเป็นประเภทอ้างอิง (กล่าวคือการอ้างอิงของคุณไปยังอินสแตนซ์คลาสเป็นตัวชี้อย่างมีประสิทธิภาพ)
ฟังก์ชันคือประเภทอ้างอิง
ทุกสิ่งทุกอย่างเป็นประเภทค่า ; "ทุกอย่างอื่น" หมายถึงอินสแตนซ์ของโครงสร้างและอินสแตนซ์ของ enum เพราะนั่นคือทั้งหมดที่มีใน Swift อาร์เรย์และสตริงเป็นอินสแตนซ์โครงสร้างตัวอย่างเช่น คุณสามารถส่งต่อการอ้างอิงไปยังสิ่งเหล่านั้น (เป็นอาร์กิวเมนต์ของฟังก์ชัน) โดยใช้inout
และรับแอดเดรสตามที่ newacct ได้ระบุไว้ แต่ประเภทนั้นเป็นประเภทค่า
วัตถุประเภทอ้างอิงมีความพิเศษในทางปฏิบัติเนื่องจาก:
การกำหนดหรือการส่งผ่านไปยังฟังก์ชันสามารถให้การอ้างอิงหลายรายการไปยังวัตถุเดียวกันได้
วัตถุนั้นไม่แน่นอนแม้ว่าการอ้างอิงถึงมันจะเป็นค่าคงที่let
ก็ตาม( ไม่ว่าจะโดยชัดแจ้งหรือโดยนัย)
การกลายพันธุ์ของวัตถุมีผลต่อวัตถุนั้นตามที่เห็นได้จากการอ้างอิงทั้งหมดกับวัตถุนั้น
สิ่งเหล่านี้อาจเป็นอันตรายได้ดังนั้นโปรดระวัง ในทางกลับกันการส่งผ่านประเภทการอ้างอิงมีประสิทธิภาพอย่างชัดเจนเนื่องจากมีการคัดลอกและส่งผ่านตัวชี้เท่านั้นซึ่งเป็นเรื่องเล็กน้อย
เห็นได้ชัดว่าการส่งผ่านประเภทค่านั้น "ปลอดภัยกว่า" และlet
หมายถึงสิ่งที่ระบุ: คุณไม่สามารถเปลี่ยนอินสแตนซ์โครงสร้างหรืออินสแตนซ์ enum ผ่านการlet
อ้างอิงได้ ในทางกลับกันความปลอดภัยนั้นเกิดขึ้นได้จากการทำสำเนาค่าแยกต่างหากใช่หรือไม่? นั่นไม่ทำให้การส่งผ่านประเภทมูลค่าอาจมีราคาแพงหรือไม่?
ใช่และไม่ใช่ มันไม่เลวร้ายอย่างที่คุณคิด ดังที่ Nate Cook ได้กล่าวไว้การส่งผ่านประเภทค่าไม่จำเป็นต้องหมายความถึงการคัดลอกเนื่องจากlet
(โดยชัดแจ้งหรือโดยนัย) รับประกันความไม่เปลี่ยนรูปดังนั้นจึงไม่จำเป็นต้องคัดลอกอะไรเลย และแม้กระทั่งการส่งต่อไปยังvar
การอ้างอิงไม่ได้หมายความว่าจะมีการคัดลอกสิ่งต่าง ๆแต่สามารถเป็นได้หากจำเป็น (เนื่องจากมีการกลายพันธุ์) เอกสารนี้แนะนำให้คุณไม่ต้องบิดกางเกงใน
inout
คำนึงถึงประเภท ไม่ว่าบางสิ่งจะเป็นแบบพาส - บาย - อ้างอิงจะตั้งฉากกับประเภทหรือไม่
ทุกอย่างใน Swift จะถูกส่งผ่านโดย "copy" ตามค่าเริ่มต้นดังนั้นเมื่อคุณส่งผ่านประเภทค่าคุณจะได้รับสำเนาของค่าและเมื่อคุณส่งผ่านประเภทการอ้างอิงคุณจะได้รับสำเนาของการอ้างอิงซึ่งหมายความว่าทั้งหมดนั้น (นั่นคือสำเนาของการอ้างอิงยังคงชี้ไปที่อินสแตนซ์เดียวกันกับการอ้างอิงดั้งเดิม)
ฉันใช้คำพูดที่ทำให้ตกใจรอบ ๆ "สำเนา" ด้านบนเพราะ Swift ทำการเพิ่มประสิทธิภาพมากมาย ทุกที่ที่เป็นไปได้จะไม่คัดลอกจนกว่าจะมีการกลายพันธุ์หรือมีความเป็นไปได้ของการกลายพันธุ์ เนื่องจากพารามิเตอร์ไม่เปลี่ยนรูปตามค่าเริ่มต้นจึงหมายความว่าส่วนใหญ่แล้วจะไม่มีการคัดลอกเกิดขึ้นจริง
มันเป็นเรื่องที่มักจะ ผ่านโดยค่าinout
เมื่อพารามิเตอร์ไม่
มันเป็นเรื่องที่มักจะ ผ่านโดยการอ้างอิงinout
ถ้าพารามิเตอร์เป็น อย่างไรก็ตามสิ่งนี้ค่อนข้างซับซ้อนเนื่องจากคุณต้องใช้ตัว&
ดำเนินการกับอาร์กิวเมนต์อย่างชัดเจนเมื่อส่งผ่านไปยังinout
พารามิเตอร์ดังนั้นจึงอาจไม่ตรงกับคำจำกัดความแบบดั้งเดิมของการอ้างอิงแบบพาสโดยอ้างอิงซึ่งคุณส่งผ่านตัวแปรโดยตรง
inout
)
inout
ไม่ได้ผ่านการอ้างอิงจริง ๆ แต่การคัดลอกในการคัดลอกออกมันรับประกันได้ว่าค่าที่แก้ไขหลังจากการเรียกฟังก์ชันจะถูกกำหนดให้กับอาร์กิวเมนต์ดั้งเดิมเท่านั้น พารามิเตอร์เข้า
นี่คือตัวอย่างโค้ดขนาดเล็กสำหรับส่งต่อโดยอ้างอิง หลีกเลี่ยงการทำเช่นนี้เว้นแต่คุณจะมีเหตุผลที่ชัดเจน
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);
inout
เป็นตัวดำเนินการคัดลอกคัดลอกออก ขั้นแรกจะคัดลอกในวัตถุจากนั้นเขียนทับวัตถุเดิมหลังจากที่ฟังก์ชันกลับมา แม้ว่าอาจดูเหมือนเหมือนกัน แต่ก็มีความแตกต่างที่ลึกซึ้ง
นักพัฒนาแอปเปิ้ลสวิฟท์บล็อกมีการโพสต์ที่เรียกว่ามูลค่าและการอ้างอิงประเภทที่ให้การสนทนาที่ชัดเจนและรายละเอียดเกี่ยวกับเรื่องนี้มาก
อ้างถึง:
ประเภทใน Swift แบ่งออกเป็นหนึ่งในสองประเภท ได้แก่ ประเภทแรก“ ประเภทค่า” โดยแต่ละอินสแตนซ์จะเก็บสำเนาข้อมูลที่ไม่ซ้ำกันโดยปกติจะกำหนดเป็นโครงสร้าง, enum หรือทูเปิล ประเภทที่สองคือ "ประเภทการอ้างอิง" โดยที่อินสแตนซ์แบ่งปันสำเนาข้อมูลเดียวและประเภทนี้มักกำหนดเป็นคลาส
บล็อกโพสต์ของ Swift ยังคงอธิบายความแตกต่างพร้อมตัวอย่างและแนะนำว่าคุณจะใช้เมื่อใด
ชั้นเรียนจะถูกส่งต่อโดยการอ้างอิงและอื่น ๆ จะถูกส่งต่อโดยค่าเริ่มต้น คุณสามารถผ่านการอ้างอิงโดยใช้inout
คำสำคัญ
inout
เป็นตัวดำเนินการคัดลอกคัดลอกออก ขั้นแรกจะคัดลอกในวัตถุจากนั้นเขียนทับวัตถุเดิมหลังจากที่ฟังก์ชันกลับมา แม้ว่าอาจดูเหมือนเหมือนกัน แต่ก็มีความแตกต่างที่ลึกซึ้ง
เมื่อคุณใช้ inout กับตัวดำเนินการ infix เช่น + = สัญลักษณ์ & address จะถูกละเว้น ฉันเดาว่าคอมไพเลอร์ถือว่าผ่านการอ้างอิง?
extension Dictionary {
static func += (left: inout Dictionary, right: Dictionary) {
for (key, value) in right {
left[key] = value
}
}
}
origDictionary + = newDictionaryToAdd
และอย่างดีพจนานุกรมนี้ 'เพิ่ม' เพียงหนึ่งเดียวที่เขียนไปยังการอ้างอิงต้นฉบับด้วยเช่นกันเหมาะสำหรับการล็อค!
ชั้นเรียนและโครงสร้าง
ความแตกต่างที่สำคัญที่สุดอย่างหนึ่งระหว่างโครงสร้างและคลาสคือโครงสร้างจะถูกคัดลอกเสมอเมื่อส่งผ่านไปในโค้ดของคุณ แต่คลาสจะถูกส่งผ่านโดยการอ้างอิง
การปิด
หากคุณกำหนดการปิดให้กับคุณสมบัติของอินสแตนซ์คลาสและการปิดจับอินสแตนซ์นั้นโดยอ้างถึงอินสแตนซ์หรือสมาชิกคุณจะสร้างวงจรอ้างอิงที่ชัดเจนระหว่างการปิดและอินสแตนซ์ Swift ใช้รายการจับภาพเพื่อทำลายวงจรอ้างอิงที่แข็งแกร่งเหล่านี้
ARC (การนับอ้างอิงอัตโนมัติ)
การนับอ้างอิงใช้กับอินสแตนซ์ของคลาสเท่านั้น โครงสร้างและการแจงนับเป็นประเภทค่าไม่ใช่ชนิดอ้างอิงและไม่ได้จัดเก็บและส่งต่อโดยการอ้างอิง