swift2 - Swift chained assignment through computed property or subscript: Where is this documented? -
i surprised find assigning member of value type through either subscript operation or computed property in swift worked 1 expect reference type: e.g. expected myarrayofvaluetype[0].somefield = value
either disallowed or no-op assign copy discarded. in fact call both getter and setter: performing mutation , assigning value type automatically.
my question is: behavior documented? can rely on behavior?
struct foo { var : int = 1 } struct fooholder { var foo : foo = foo() var afoo : foo { { return foo } set { foo = newvalue } } subscript(i: int) -> foo { { return foo } set { foo = newvalue } } } var fh = fooholder() fh.afoo.a // 1 fh.afoo.a = 42 // equivalent: var foo = fh.afoo; foo.a = 42; fo.afoo = foo fh.afoo.a // 42! // same true of subscripts var fh = fooholder() fh[0].a // 1 fh[0].a = 42 fh[0].a // 42!
edit:
to state question in way:
swift makes both subscript , computed property access transparent far value type copying concerned. regular method call return copy of value type swift appears perform two-step dance using both getter , setter @ different phases of evaluation produce value, mutate it, , set back. seems would/should documented if no other reason totally non-obvious , have side effects in poorly written code.
i'm having hard time finding explicit documentation of behavior. there's little bit of hinting @ in computed properties in swift book, it's worth being stated more upfront. i'd recommend filing bug against documentation.
in meantime — yes, can rely on behavior, seem pretty fundamental design of swift. can 2 pretty safe assumptions swift's design philosophy:
- basic value type data structures should usable , accessible in other c-family languages.
- computed properties should caller's perspective behave same stored properties.
for #1, consider following (obj)c example:
cgrect rect = cgrectmake(0, 0, 320, 480); // cgrect nested structure: {origin: {x, y}, size: {w, h}} rect.origin.y = 20; rect.size.height = 460; // rect {{0, 0}, {320, 460}}
this works because c struct syntactic sugar pointer math. cgrect
contiguous block of 4 floating-point values. (as nested struct, it's block of 2 smaller blocks, blocks of values.) when read or write origin.y
compiler uses struct's definition determine in memory block read or write single float, regardless of whether that's memory statically allocated on stack (a function parameter or local variable) or dynamically allocated on heap.
swift needs able work data structures originating in or passed c apis, can expect basic value type structures work pretty same in c, modulo mutability restriction of var
vs let
. consider extending cgrect
example involve array of rects:
cgrect rects[10] = /*...*/; rects[5].size.height = 23;
again, "just works" in c because it's pointer arithmetic handled compiler. rects
contiguous chunk of memory; find offset chunk of 6th sub-chunk (a rect / block of 4 floats); find offset sub-chunk of size
field (a size / block of 2 floats); find offset that of height
field; write location. swift needs interoperate c (not mention memory efficient itself), "just works" in swift, too.
for #2, reading between lines bit in computed properties in swift book hints pretty computed properties should act stored properties caller's perspective.
to borrow example, let's extend cgrect
add computed center
property:
var center: cgpoint { { let centerx = origin.x + (size.width / 2) let centery = origin.y + (size.height / 2) return cgpoint(x: centerx, y: centery) } set(newcenter) { origin.x = newcenter.x - (size.width / 2) origin.y = newcenter.y - (size.height / 2) } }
if have our array of cgrect
s in swift, should able set center
can set origin
:
rects[4].origin.x = 3 rects[6].center.x = 5
and indeed, works. caller's perspective, center
property — don't have care whether it's stored or computed.
this critical part of abstraction: protocol merely declare these properties exist (and readwrite or readonly), , 2 different structs adopting protocol implement center
stored , origin
computed or vice versa.
how works case of swift compiler doing more c compiler, same philosophy. c compiler sees accesses structure members , pointer math, swift compiler sees accesses , inserts function calls work through pointer indirection underlying values. it's if following functions existed in c:
inline cgpoint cgrectgetcenter(cgrect rect) { return cgpointmake(rect.origin.x + (size.width / 2), rect.origin.y + (size.height / 2) } inline void cgrectsetcenter(cgrect *rect, cgpoint newcenter) { rect->origin.x = newcenter.x - (rect->size.width / 2); rect->origin.y = newcenter.y - (rect->size.height / 2); }
...and compiler automagically turned reads , writes rect.center
calls functions:
cgrect rect = cgrectmake(0, 0, 320, 480); cgrectgetcenter(rect); // {160, 240} cgrectsetcenter(&rect, cgpointmake(0, 0)); // rect {{-160, -240}, {320, 480}}
(and notice in c, functions work regardless of whether passed rect or pointer-to-rect in array or nested structure.)
the "magic" part swift applies such transformations way down — if struct includes struct, redirection of computed property accessors through functions works way down, computed properties work stored properties caller's perspective.
it works inout
function parameters. +=
, (while it's still around) ++
operators functions inout
parameters, can following:
rects[7].origin.y += 10 rect.center.x++
each time use function inout
parameter, compiler emits necessary pointer math or function calls read current value of member, calls function, , reverse set of pointer math / function calls put result in place. rect.center.x++
calls cgrect.center.get
, pokes value place in resulting cgpoint
struct, calls cgrect.center.set
.
(this part bit more thoroughly documented @ in-out burgersparameters in swift book.)
Comments
Post a Comment