ฉันจะสร้างสำเนาอาร์เรย์ที่ซ้ำกันได้อย่างไร


101

ฉันจะสร้างอาร์เรย์ที่ซ้ำกันได้อย่างไร

ฉันมีปัญหาในการค้นหาข้อมูลเกี่ยวกับการทำซ้ำอาร์เรย์ใน Swift

ฉันลองใช้ .copy()

var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()

5
ทำไมคุณไม่กำหนดมูลค่าโดยตรงเช่นนี้var duplicateArray = originalArray
Dharmesh Kheni

1
ไม่ได้ผลในกรณีของฉัน ที่สร้างออบเจ็กต์อื่นที่เป็นเพียงการอ้างอิงไปยังอาร์เรย์เดียวกันและคุณท้ายด้วย 2 ตัวแปรที่อ้างอิงอาร์เรย์เดียวกัน
user1060500

คำตอบ:


178

อาร์เรย์มีความหมายเต็มรูปแบบใน Swift ดังนั้นจึงไม่จำเป็นต้องมีอะไรแปลกใหม่

var duplicateArray = originalArray คือทั้งหมดที่คุณต้องการ


หากเนื้อหาของอาร์เรย์ของคุณเป็นประเภทการอ้างอิงใช่สิ่งนี้จะคัดลอกตัวชี้ไปยังวัตถุของคุณเท่านั้น ในการคัดลอกเนื้อหาแบบละเอียดคุณจะต้องใช้mapและคัดลอกอินสแตนซ์แต่ละรายการแทน สำหรับคลาสพื้นฐานที่เป็นไปตามNSCopyingโปรโตคอลคุณสามารถใช้copy()วิธีการ:

let x = [NSMutableArray(), NSMutableArray(), NSMutableArray()]
let y = x
let z = x.map { $0.copy() }

x[0] === y[0]   // true
x[0] === z[0]   // false

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


ฉันลองสิ่งนี้ในสนามเด็กเล่นด้วยรหัสง่ายๆนี้var x = [UIView(), UIView(), UIView()] var y = x for i in x { NSLog("%p", i) } println("---") for i in y { NSLog("%p", i) }และฉันได้ผลลัพธ์นี้: 0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ---0x7fa82b0009e0 0x7fa82b012660 0x7fa82b012770 ดูเหมือนจะไม่ถูกคัดลอกคุณรู้ไหมว่าทำไม?
Phil Niedertscheider

@PNGamingPower: x มีที่อยู่ y มีสำเนาของที่อยู่เหล่านั้น หากคุณเปลี่ยน x [0] y [0] จะไม่เปลี่ยนแปลง (ลอง x [0] = x [1], y [0] จะไม่เปลี่ยนแปลง) ดังนั้น y จึงเป็นสำเนาลึกของ x แต่คุณคัดลอกเฉพาะพอยน์เตอร์เท่านั้นไม่ใช่สิ่งที่ชี้ไป
ragnarius

@ragnarius โดยพื้นฐานแล้วเราต้องกำหนดความหมายของ "สำเนา" ไม่ว่าจะเป็นการคัดลอกตัวชี้หรือค่า ดังนั้นนี่จึงเป็นทางออกสำหรับการคัดลอก / ทำซ้ำอาร์เรย์ของพอยน์เตอร์ แต่คุณจะทำซ้ำอาร์เรย์ของค่าได้อย่างไร? เป้าหมายจะเป็นไปได้x[0] == x[1]แต่x[0] === y[0]ควรล้มเหลว
Phil Niedertscheider

นี่ควรเป็นคำตอบที่ยอมรับเนื่องจากความหมายของค่าของ Array ทำให้ "สำเนา" ของอาร์เรย์ไม่จำเป็น
Scott Ahten

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

29

เนทถูกต้อง หากคุณกำลังทำงานกับอาร์เรย์ดั้งเดิมสิ่งที่คุณต้องทำคือกำหนด duplicateArray ให้กับ originalArray

เพื่อความสมบูรณ์หากคุณใช้งานออบเจ็กต์ NSArray คุณจะต้องทำสิ่งต่อไปนี้เพื่อทำสำเนา NSArray แบบเต็ม:

var originalArray = [1, 2, 3, 4] as NSArray

var duplicateArray = NSArray(array:originalArray, copyItems: true)

นี่มันเยี่ยมมาก! ขอบคุณ!
Patrick

25

มีตัวเลือกที่สามสำหรับคำตอบของ Nate:

let z = x.map { $0 }  // different array with same objects

* แก้ไข *แก้ไขเริ่มต้นที่นี่

ด้านบนจะเหมือนกับด้านล่างและการใช้ตัวดำเนินการความเท่าเทียมกันด้านล่างจะทำงานได้ดีกว่าเนื่องจากอาร์เรย์จะไม่ถูกคัดลอกเว้นแต่จะมีการเปลี่ยนแปลง (นี่คือการออกแบบ)

let z = x

อ่านเพิ่มเติมที่นี่: https://developer.apple.com/swift/blog/?id=10

* แก้ไข *แก้ไขสิ้นสุดที่นี่

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


มันมีผลฉันได้ทดสอบแล้ว มีสองอาร์เรย์ถ้าคุณเปลี่ยนใน 1 อันที่สองจะมีผล
Filthy Knight

1
ไม่เป็นเช่นนั้นเว้นแต่อาร์เรย์จะมีประเภทดั้งเดิมแทนที่จะเป็นวัตถุ จากนั้นจะมีผลตามที่ระบุไว้ในคำตอบ กรณีทดสอบง่ายๆ:var array1: [String] = ["john", "alan", "kristen"]; print(array1); var array2 = array1.map { $0 }; print(array2); array2[0] = "james"; print(array1); print(array2);
oyalhi

1
โปรดดูส่วนสำคัญที่ฉันสร้างขึ้นเพื่อเป็นตัวอย่างที่ดีกว่าโดยใช้คลาสที่กำหนดเอง: gist.github.com/oyalhi/3b9a415cf20b5b54bb3833817db059ce
oyalhi

หากคลาสของคุณรองรับNSCopyingให้ทำซ้ำอาร์เรย์:let z = x.map { $0.copy as! ClassX }
John Pang

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

16

สำหรับวัตถุปกติสิ่งที่สามารถทำได้คือการใช้โปรโตคอลที่รองรับการคัดลอกและทำให้คลาสอ็อบเจ็กต์ใช้โปรโตคอลนี้ดังนี้:

protocol Copying {
    init(original: Self)
}

extension Copying {
    func copy() -> Self {
        return Self.init(original: self)
    }
}

จากนั้นส่วนขยาย Array สำหรับการโคลน:

extension Array where Element: Copying {
    func clone() -> Array {
        var copiedArray = Array<Element>()
        for element in self {
            copiedArray.append(element.copy())
        }
        return copiedArray
    }
}

และนั่นก็ค่อนข้างดีในการดูโค้ดและตัวอย่างตรวจสอบส่วนสำคัญนี้


ช่วยประหยัดเวลาได้มากขอบคุณ
Abhijit

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

1
สำเนา @iGodric ใช้สำหรับองค์ประกอบในคอลเลกชันและการโคลนมีไว้สำหรับคอลเลกชันทั้งหมดพร้อมใช้งานเมื่อมีการนำสำเนาไปใช้กับองค์ประกอบ เข้าท่า? นอกจากนี้คอมไพเลอร์ยังตรวจสอบให้แน่ใจว่าคลาสย่อยเป็นไปตามโปรโตคอลที่ผู้ปกครองต้องการ
johnbakers

@johnbakers โอ้ใช่ตอนนี้ฉันเห็นแล้ว ขอบคุณสำหรับคำอธิบาย
Binarian

การใช้งานที่สะอาดมากและหลีกเลี่ยงความเร่งรีบที่ไม่จำเป็นในการส่งผ่านพารามิเตอร์ใด ๆ ในobject'sฟังก์ชัน init
Sylvan D Ash

0

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

class ABC {
    
    var a = 0
    func myCopy() -> ABC {
        
        return ABC(value: self.a)
    }
    
    init(value: Int) {
        
        self.a = value
    }
}

var arrayA: [ABC] = [ABC(value: 1)]
var arrayB: [ABC] = arrayA.map { $0.myCopy() }

arrayB.first?.a = 2
print(arrayA.first?.a)//Prints 1
print(arrayB.first?.a)//Prints 2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.