On Sat, Dec 22, 2012 at 3:15 PM, Paul M. [email protected]
wrote:
myString3 has been “corrupted”, presumably because setting myString4 to
it actually set myString4’s pointer, not its value, in the standard OO
fashion.
There’s a subtle point here:
myString3 = “Fred Nerk”
= is a built in piece of syntax to bind a variable to an object.
Variables are not themselves objects, they are transparent references
to objects. The only time a variable cannot be transparently replaced
by the object it refers to is when it’s on the left hand side of an
equal sign. So here, “Fred Nerk” uses the string literal to create the
string object #<String:0x01234567 “Fred Nerk”> [that is, an object
with type String, object_id 0x01234567 and value “Fred Nerk”] on the
heap, and binds the variable myString3 to it.
myString4 = myString3
Here, myString3 is transparently replaced by the object it refers to,
#<String:0x01234567 “Fred Nerk”>, and myString4 is bound to the same
object
myString4[0,4] = “Bert”
This is the subtle bit. Despite the syntactic sugar, this is not an
= sign. There is no variable binding = involved here, it is just ruby
syntax sugar that gets rewritten to myString.[]=(0, 4, “Bert”). That
is, it calls the “[]=” method on the string object, passing it values
(0, 4, “Bert”). Again, since myString4 is not on the left hand side of
an =, it gets transparently replaced by #<String:0x01234567 “Fred
Nerk”>, which then gets sent the message []= with arguments (0, 4,
“Bert”), and obligingly updates its value. So our object is now
#<String:0x01234567 “Bert Nerk”> (note that the object hasn’t changed,
just its value).
=> myString3 = “Bert Nerk”
=> myString4 = “Bert Nerk”
Again, these are both transparently replaced by the object they refer
to, now #<String:0x01234567 “Bert Nerk”>
But
c) String literal
myString1 = “Fred Bloggs”
Creates #<String:0x98765432 “Fred Bloggs”> on the heap, binds myString1
to it.
myString2 = myString1
Binds myString2 to #<String:0x98765432 “Fred Bloggs”>
myString2[0,4] = “Bert”
Sends []=, (0, 4, “Bert”) to #<String:0x98765432 “Fred Bloggs”>, which
updates itself to #<String:0x98765432 “Bert Bloggs”>
puts "Fred Bloggs = " + “Fred Bloggs”
Creates two new string object, #<String:0x00001111 "Fred Bloggs = ">
and #<String:0x00001112 “Fred Bloggs”> and passes the second one as an
argument to the + method of the first, which returns yet another
string object, #<String:0x00001113 “Fred Bloggs = Fred Bloggs”> which
it passes to “puts” which prints it out.
puts "myString2 = " + myString2
Creates one new string object, #<String:0x00001114 "myString2 = ">,
and calls its + method with #<String:0x98765432 “Bert Bloggs”> (the
transparent replacement for myString2) as an argument. This creates
yet another string object, #<String:0x00001115 “myString2 = Bert
Bloggs”>, which gets passed to puts and printed out.
[Note that all the string objects that got created but never had
variables bound to them are temporary objects that the garbage
collector will take care of at some point]
So how to get around this? The following appears to do it:
f) String constant 3
MyString8 = “Fred Shufflebotham”
myString9 = MyString8.clone
Clone creates a new string object, and sets its value equal to that of
the first one. = then binds myString9 to this new object.
myString9[0,4] = “Bert”
the []=, 0, 4, “Bert” message is getting sent to the new object
=> MyString8 = “Fred Shufflebotham”
myString8 is still bound to the first object, which never got sent a
message.
=> myString9 = “Bert Shufflebotham”
myString9 is still bound to the new object, which did get sent the
[]= message and updated its value
but doesn’t it cause a memory leak?
No, the garbage collector takes care of it.
martin