rails 프로젝트를 하다가 객체를 만들어 concat을 사용하는 메소드를 실행 시키는데
초기 config값을 가지고 있는 변수의 값이 자꾸만 변하는것이다. 예를 들면
class Test
CONFIG = "1111"
def print
test1 = CONFIG
test1.concat("2222")
puts test1
puts CONFIG
end
end
이런 클래스가 있다고 치자 이때 CONFIG 변수에 저장된 값을 test1에 저장하고
test1에 문자열을 더하고 싶어 concat을 하여 2222를 추가 하였다. 그리고 test1과 CONFIG를 출력하였다.
이때 출력값은 무엇이 나올까?
놀랍게도 두개의 출력값다 기존에 있던 1111값과 concat한 2222를 한 값인 11112222가 나오게 된다.
사실 이건 concat의 문제는 아니다.
다만 내가 concat을 사용할때 생긴 문제이기 때문에 예를 이것으로 든것 뿐이다.
그래서 왜 이런 일이 생긴것인지에 대해서 찾아보았다.
먼저 결론부터 말하자면 루비에서 변수는 객체 이기 때문에 이런 현상이 생긴것이다.
(읽기 귀찮으면 밑에 3줄요약만 읽어도됨)
루비는 모든것이 객체인가?
이 현상의 원인은 루비의 자체에 있는데
루비는 모든것이 객체이다. 그래서 정말 변수도 객체인것인가 해서 찾아보았다.
라는 제목으로 누군가가 질문을 한것에 답이있는데 요약하자면
루비의 모든것이 객체 이다 하지만 변수는 객체를 가리키는 이름이다라고 했다.
음.. 무슨 말인지는 모르겠지만 어쨋든 "변수는 객체를 가리키는 것이고 변수 자체는 객체가 아니다" 라는게 대답의 요점인것 같다.
그러니까 처음에 위에서 선언한 변수는 객체가 아니지만 CONFIG 에 저장한 "1111"이라는 문자열은 루비에서 string객체로 인식을 하고 객체(1111)의 주소값을 저장한것이다.
고로
test1 = CONFIG
이러한 행위는 CONFIG변수 안에 담고있는 string객체(1111)의 주소값을 test1에 저장하는 행위가 되는것이다.
하지만 아무리 변수가 객체를 가지고 있는다고 해도 변수를 다른 변수에 저장할때 객체의 값을 복사할수도 있는데 왜 주소값이 복사가 되는것일까 라는 의문점이 생겼다.
루비는 call by references 방식 인가?
그래서 루비는 call by references 방식인건가 라는 생각에 한번더 검색을 해봤다.
(call by references가 무엇인지에 대해서 궁금하면
이곳 에 들어가면 간단하게 정리되어있다. call by value에 대한 설명과 둘의 차이점도 정리되어있다.)
두개의 답변을 볼수 있는데 요약 하자면
"변수는 항상 객체를 참조(references)한다. 루비에서 변수는 객체를 가리키는(point)존재 이므로 객체를 가리킬수 있는 주소값을 변수에 저장하는 것이고 그 값을 복사하는 것이다. 그래서 루비는 call by value 이지만 value에 references(객체의 주소값)를 가지고 있기때문에 주소값이 복사되는것이다."
라는게 요약이다. 뭔가 어렵다...
간단히 말하자면
루비는 call by references 방식이 아니고 call by value 방식으로 변수를 복사 하지만 복사할 value에 주소값이 들어있을 뿐이다.
결론적으로 루비는 call by references 방식을 가지고 있어서, 주소값을 복사해주는게 아니라 value 가 주소값이라서 주소값을 복사하는 것이다. 예를들면
a = 1 <-- a가 가리키는 객체(1)의 주소값 111
b = a <-- a가 가리키는 객체(1)의 주소값 111을 b에 저장
puts b <--- b가 가리키는 객체(1)의 주소값 111에 저장된 값 출력
이렇게 된다는 것이다.
그래서 b의 값을 변경하면 a의 값도 변경이 되는 거다.
루비에서 변수의 값(객체의 값) 복사 하기
위에서 루비의 변수는 객체의 주소값을 가지고 있기 때문에 복사당한 대상의 변수값도 변경이 될수있다고 했다.
하지만 나는 변수의 값(객체의 값)을 복사해서 사용하고 싶다.
이럴때는 clone이라는 함수를 사용하면된다. 예를들면
test = [1,2,3]
b = a.clone
b.concat 4
puts a
puts b
를하면 a는 123이 출력되고 b는 1234가 출력된다.
더 자세하게 알고 싶다면 이곳 에서 cloning objects라는 부분을 보면된다.
결국 루비에서 변수는 객체를 가지고 있는 것이고, 변수끼리 값을 전달할때 변수에 저장된 값은 객체의 주소값을 가지고 있기 때문에 주소값이 전달이 된다. 그러므로 변수의 값(객체의 값)을 복사해서 사용하고 싶다면 clone 메소드를 사용하면된다.
3줄 요약
1. 루비에서 변수는 객체를 가리키(point)기 때문에 주소값을 가짐
2. 그래서 변수 복사를 할때 call by value 방식이지만 주소값이 넘어가서 메모리를 공유하게됨
3. 이게 싫으면 clone메소드를 쓰면됨
참고
http://stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value
http://stackoverflow.com/questions/22827566/ruby-parameters-by-reference-or-by-value/22827949#22827949
http://stackoverflow.com/questions/8382173/ruby-is-variable-is-object-in-ruby
https://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/39-ruby-s-object-model/lessons/132-cloning-and-freezing-objects