디자인 패턴 | 프로토타입 패턴 (prototype pattern) + 깊은복사와 얕은 복사(값타입과 참조타입)
iOSDEv
2024. 1. 31. 18:47
생성패턴(Creatioanl Pattern)
객체 인스턴스를 생성하는 패턴 클라이언트와 클라이언트가 생성해야하는 객체 인스턴스 사이의 연결을 끊어주는 패턴객체가 생성되는 과정의 유연성을 높이고 코드의 유지를 쉽게 만들어주는 패턴
우선 시작하기전 알아야할 지식!
깊은 복사와 얕은 복사란?
프로토타입 패턴을 사용하기전 얕은 복사와 깊은 복사에 대해서 알아야한다 - 얕은 복사 : Reference Type복사시 사용하는 방법 : 가리키고 있는 인스턴스의 주소값만으로 공유하고 같은 인스턴스를 가리키게 하는 방식 : 복사본을 수정한다고해도 원본데이터에도 영향(동일한 주소값을 가지기 때문)이 가기 때문에 주의가 필요함
-깊은 복사 : Value Type복사시 사용하는 방법 : 데이터 자체를 복사하여 독립적인 메모리 공간을 차지해서복사본을 수정해도 원본에는 영향이 전~혀 가지않음
주소를 가져오는 방법
- UnsafeRawPointer : 변수에 저장된 값이 어떤 Type인지에 대한 정보가 없고 주소값만 가져올 수 있음
- UnsafePointer : 변수에대한 주소와 타입의 정보를 가지고 있음
깊은 복사란(Deep copy)
쉽게말해 그냥 데이터 자체를 통으로 복사한다고 생각하면됨
깊은 복사는 value Type(struct / enum / array / tuple)의 객체들이 하게됨
복사된 두 객체는 완전히 다른 독립적인 메모리를 차지하게된다 (그냥 남남이라고 생각하면됨)
초반에 같은 주소값을 각 변수가 가지고 있다가 둘 중 하나의 변수를 다른 값으로 수정하게되면 그 변수에 메모리를 새로 할당하게되면서 주소값이 변경됩니다
그리고 먼저 수정되는 변수가 메모리의 주소값이 변경된다는 사실
불필요한 복사를 더 줄이고 싶다면 얕은복사!
얕은복사란(Shallow copy)
그냥 최소한의 복사만 한다고 생각하면되는데 복사한다고 해도 인스턴스가 메모리에 새로 생성되질 않음
why? 값을 복사하는게 아니라 주소값을 복사하기 때문에 같은 메모리를 가리키게됨 (즉 동일한 주소값을 복사하게됨)
새로운 인스턴스를 생성하지 않기 때문에 깊은 복사보다 빠름
reference Type(class / closure)에서 복사하는 경우에 얕은 복사가 발생함
++++++++
Reference Type은 얕은복사인데 깊은 복사가 된다고?
참조타입의 객체를 복사하면 새로운 객체가 생성되지 않고 주소값이 복사되는데 분리된 객체를 생성하고 싶다면?
NSCopying 프로토콜을 채택하여 copy() method를 이용하게되면 깊은 복사가 가능하게 된다
reference Type(참조타입)의 깊은 복사
reference type 내에 reference type을 가지고 있는 경우
reference type 내에 reference type을 가지고 있는 경우의 주소를 확인할 수 있다
배열이 작을 때는 배열 인덱스 하나당 메모리를 할당받아도 문제가 없지만 만약 아주 큰 크기의 배열을 할당하게되고 복사하게되면 똑같은 배열을 한번 더 할당하게되는데 이건 너무 비효율적임 ( 만약 1000000개의 크기의 배열을 복사한다고 하면... 인덱스당 다른 메모리 주소값을 할당받기 때문에 1000000개의 주소 + 1000000개의 주소를 받게되는건 너무 비효율적임)
1000000개의 주소 +1000000개 = 2000000개의 주소(x)
건드리기 전까진 그냥 1000000개의 주소 그대로 가지고 있다고 생각하면됨
프로토타입 패턴
코드를 클래스에 종속시키지 않고 기존에 존재하던 객체를 복사할 수 있는 디자인 패턴
interface(타언어에서 사용하는것) == Protocol이라고 생각하면 됩니다
사용하는 경우?
클래스 수를 줄이며 사용하고 싶을 때 사용하면 유용할것 같다
설정하는데 몇 가지 적용해야하는 클래스를 새로 생성해야할 경우 중복을 줄이기 위해 사용하면 간편할것 같다
장점
새로운 객체 생성이 간단 : 기존 객체를 복제함으로 객체 생성이 간단해진다
복잡한 초기화 과정을 피할 수 있음 : 객체의 생성 과정이 복잡한 겨우 이미 초기화된 객체를 복제함으로 초기화 과정을 간편하게 처리할 수 있음
생성비용 절감 : 객체를 복제하는것이 객체를 새로 생성하는것보다 효율적일 수 있음
단점
클래스 계층 구조 복잡성 : 프로토타입 패턴을 사용하면 객체의 클래스 계층 구조가 복잡해질 수 있음
깊은 복사 어려움 : 객체가 내부에 참조 타입을 가지고 있을 경우 얕은 복사로는 원하는 결과를 얻기 어려울 수 있음
chat gpt 예시
// Prototype 인터페이스
protocol Prototype {
func clone() -> Prototype
}
// ConcretePrototype: 구체적인 프로토타입 클래스
class ConcretePrototype: Prototype {
var property: Int
init(property: Int) {
self.property = property
}
// 복제 메서드
func clone() -> Prototype {
return ConcretePrototype(property: self.property)
}
}
// Client
class Client {
func operation(prototype: Prototype) {
// 프로토타입을 복제하여 새로운 객체 생성
let newObject = prototype.clone()
// 복제된 객체의 속성 확인
if let concreteObject = newObject as? ConcretePrototype {
print("Cloned object property: \(concreteObject.property)")
}
}
}
// 사용 예제
let originalPrototype = ConcretePrototype(property: 42)
let client = Client()
// 클라이언트가 프로토타입을 사용하여 객체 생성
client.operation(prototype: originalPrototype)
구현하는 방법 1. 프로토타입(인터페이스) 생성하고 clone()메서드나 복제되어야하는 객체에 대한 인터페이스를 정의하고 객체를 복제하는 메서드를 제공한다 2.ConcretePrototype(구체적인 프로토타입) 프로토타입 인터페이스를 구현한 실제 객체로 복제될 때 원본 객체 복사본을 생성 3.Client에서는 프로토타입 객체를 사용하여 새로운 객체를 생성하는 역할을 하고 클라이언트는 프로토타입 객체를 복사하여 새로운 객체를 얻을 수 있음
즉 ConcretePrototype 클래스가 Prototype 프로토콜을 따르고, 클라이언트에서는 프로토타입 객체를 받아서 clone 메서드를 호출하여 새로운 객체를 생성합니다. 이렇게 하면 새로운 객체를 만들기 위해 복잡한 초기화 과정을 반복하지 않고 기존 객체를 복제하여 효율적으로 객체를 생성