objective c - Handling Pointer-to-Pointer Ownership Issues in ARC -
objective c - Handling Pointer-to-Pointer Ownership Issues in ARC -
suppose object a has property:
@property (nonatomic, strong) foo * bar;
synthesized in implementation as:
@synthesize bar = _bar;
object b manipulates foo **
, in illustration phone call object a:
foo * temp = self.bar; [objb dosomething:&temp]; self.bar = temp;
can this, or similar, done legitimately? what right declaration dosomething:
method? furthermore, suppose object b may deallocated before have chance set bar
property (and take on ownership of instance pointed temp
) - how tell arc hand off owning reference? in other words, if wanted next illustration snippet work, how need handle arc issues?
foo * temp = self.bar; // give reference current value [objb dosomething:&temp]; // allow modify reference self.bar = nil; // release whatever have _bar = temp; // since we're getting owning reference, bypass setter
what aren't thinking of? edit
based on @kevinballard 's answer, want confirm understanding. correct?
object a:
@implementation objecta @synthesize bar = _bar; - (void)somemethod { objectb * objb = [[objectb alloc] initwithfoo:&_bar]; // objb handed off somewhere , it's "dosomething" method called. } @end
object b:
@implementation objectb { foo * __autoreleasing * _temp; } - (id)initwithfoo:(foo * __autoreleasing *)temp { id self = [super init]; if (self) { _temp = temp; } homecoming self; } - (void)dosomething { ... *_temp = [[foo alloc] init]; ... } @end
this creates compile-time error: passing address of non-local object __autoreleasing parameter write-back
arc needs know ownership of object reference can determine when release etc. variable (local, instance or global) arc has rules determining ownership; either inference or explicit attribute. equates pre-arc need programmer track ownership.
but happens if have reference variable? not (pre-arc) write code accepted reference variable , work correctly regardless of ownership of variable - not know whether needed release etc. i.e. can not build code works variable (in sense of changing!) unknown ownership.
arc faces same problem , solution infer, or take explicit attribute specifying, ownership of referenced variable , require caller arrange reference variable of appropriate ownership passed. latter bit can require utilize of hidden temporary variables. "least bad solution" referred , termed "pass-by-writeback".
the first part of question:
foo * temp = self.bar; [objb dosomething:&temp]; self.bar = temp;
can this, or similar, done legitimately? yes, code fine arc. temp
inferred strong
, behind scenes stuff happens pass reference dosomething:
.
- (void) dosomething:(foo **)byreffoo
arc infers byreffoo
of type foo * __autoreleasing *
- reference autoreleasing reference. required "pass-by-writeback".
this code only valid because temp
local. wrong instance variable (as found out in edit). only valid assuming parameter beingness used in standard "out" mode , updated value has been assign when dosomething:
returns. both of these because way pass-by-writeback works part of "least bad solution"...
summary: when using local variables can passed reference utilize in standard "out" pattern arc inferring required attributes etc.
under hoodinstead of foo
of question we'll utilize type breadcrumbs
; wrapped nsstring
tracks every init
, retain
, release
, autorelease
, dealloc
(well you'll see below) can see going on. how breadcrumbs
written not material.
now consider next class:
@implementation byref { breadcrumbs *instance; // __strong inferred }
a method alter value passed reference:
- (void) indirect:(breadcrumbs **)byref // __autoreleasing inferred { *byref = [breadcrumbs newwith:@"banana"]; }
a simple wrapper indirect:
can see passed , when returns:
- (void) indirectwrapper:(breadcrumbs **)byref // __autoreleasing inferred { nslog(@"indirect: passed reference %p, contains %p - %@, owners %lu", byref, *byref, *byref, [*byref ownercount]); [self indirect:byref]; nslog(@"indirect: returned"); }
and method deomnstrate indirect:
called on local variable (called imaginatively local
):
- (void) demo1 { nslog(@"strong local passed autoreleasing reference"); breadcrumbs *local; // __strong inferred local = [breadcrumbs newwith:@"apple"]; nslog(@"local: addr %p, contains %p - %@, owners %lu", &local, local, local, [local ownercount]); [self indirectwrapper:&local]; nslog(@"local: addr %p, contains %p - %@, owners %lu", &local, local, local, [local ownercount]); } @end
now code exercise demo1
localizing autorelease pool can see allocated, released , when:
byref *test = [byref new]; nslog(@"start demo1"); @autoreleasepool { [test demo1]; nslog(@"flush demo1"); } nslog(@"end demo1");
executing above produces next on console:
ark[2041:707] start demo1 ark[2041:707] strong local passed autoreleasing reference ark[2041:707] >>> 0x100176f30: init ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1 ark[2041:707] indirect: passed reference 0x7fff5fbfedb8, contains 0x100176f30 - apple, owners 1 ark[2041:707] >>> 0x100427d10: init ark[2041:707] >>> 0x100427d10: autorelease ark[2041:707] indirect: returned ark[2041:707] >>> 0x100427d10: retain ark[2041:707] >>> 0x100176f30: release ark[2041:707] >>> 0x100176f30: dealloc ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100427d10 - banana, owners 2 ark[2041:707] >>> 0x100427d10: release ark[2041:707] flush demo1 ark[2041:707] >>> 0x100427d10: release ark[2041:707] >>> 0x100427d10: dealloc ark[2041:707] end demo1
[the ">>>" lines come breadcrumbs
.] follow addresses of objects (0x100...) , variables (0x7fff...) , clear...
well maybe not! here 1 time again comments after each chunk:
ark[2041:707] start demo1 ark[2041:707] strong local passed autoreleasing reference ark[2041:707] >>> 0x100176f30: init ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1
here see [breadcrumbs newwith:@"apple"]
creates object @ address 0x100176f30
. stored in local
, address 0x7fff5fbfedc0
, , object has 1 owner (local
).
ark[2041:707] indirect: passed reference 0x7fff5fbfedb8, contains 0x100176f30 - apple, owners 1
here comes hidden variable: indirect:
requires reference autoreleasing variable arc has created new variable, address 0x7fff5fbfedb8
, , copied object reference (0x100176f30
) that.
ark[2041:707] >>> 0x100427d10: init ark[2041:707] >>> 0x100427d10: autorelease ark[2041:707] indirect: returned
inside indirect:
new object created , arc autoreleases before assigning - because passed references refers autoreleasing variable.
note: arc not need previous contents (0x100176f30
) of referenced variable (0x7fff5fbfedb8
) autoreleasing , hence not responsibility. i.e. "autoreleasing ownership" means reference assigned must have been effectively autoreleased. you'll see when creating hidden variable arc did not retain , autorelease contents - did not need knows there strong reference (in local
) object managing. [in lastly illustration below arc not elide retain/autorelease.]
ark[2041:707] >>> 0x100427d10: retain ark[2041:707] >>> 0x100176f30: release ark[2041:707] >>> 0x100176f30: dealloc
these actions result copying (the "writeback" in call-by-writeback) value hidden variable local
. release/dealloc old strong reference in local
, , retain object referenced hidden variable (which autoreleased indirect:
)
note: writeback why works "out" pattern of using pass-by-reference - can't store reference passed indirect:
hidden local variable disappear...
ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100427d10 - banana, owners 2
so after phone call local
refers new object, , has 2 owners - local
accounts one, , other autorelease
in indirect:
ark[2041:707] >>> 0x100427d10: release
demo1
finished arc releases object in local
ark[2041:707] flush demo1 ark[2041:707] >>> 0x100427d10: release ark[2041:707] >>> 0x100427d10: dealloc ark[2041:707] end demo1
and after demo1
returns localized @autoreleasepool
handles autorelease pending indirect:
, ownership 0 , dealloc
.
passing instance variables reference
the above deals passing local variables reference, unfortunately pass-by-writeback does not work instance variables. there 2 basic solutions:
copy instance variable local
add attributes
to demonstrate sec add together class byref
strongindirect:
specifies requires reference strong variable:
- (void) strongindirect:(breadcrumbs * __strong *)byref { *byref = [breadcrumbs newwith:@"plum"]; } - (void) strongindirectwrapper:(breadcrumbs * __strong *)byref { nslog(@"strongindirect: passed reference %p, contains %p - %@, owners %lu", byref, *byref, *byref, [*byref ownercount]); [self strongindirect:byref]; nslog(@"strongindirect: returned"); }
and corresponding demo2
uses byref
's instance variable (again imaginative name of instance
):
- (void) demo2 { nslog(@"strong instance passed strong reference"); instance = [breadcrumbs newwith:@"orange"]; nslog(@"instance: addr %p, contains %p - %@, owners %lu", &instance, instance, instance, [instance ownercount]); [self strongindirectwrapper:&instance]; nslog(@"instance: addr %p, contains %p - %@, owners %lu", &instance, instance, instance, [instance ownercount]); }
execute similiar piece of code demo1
above , get:
1 ark[2041:707] start demo2 2 ark[2041:707] strong instance passed strong reference 3 ark[2041:707] >>> 0x100176f30: init 4 ark[2041:707] instance: addr 0x100147518, contains 0x100176f30 - orange, owners 1 5 ark[2041:707] strongindirect: passed reference 0x100147518, contains 0x100176f30 - orange, owners 1 6 ark[2041:707] >>> 0x100427d10: init 7 ark[2041:707] >>> 0x100176f30: release 8 ark[2041:707] >>> 0x100176f30: dealloc 9 ark[2041:707] strongindirect: returned 10 ark[2041:707] instance: addr 0x100147518, contains 0x100427d10 - plum, owners 1 11 ark[2041:707] flush demo2 12 ark[2041:707] end demo2
which bit shorter before. 2 reasons:
as passing strong variable (instance
) method (strongindirect:
) expects reference strong variable there no need arc utilize hidden variable - variables in line 4 , 5 above same (0x100147518
).
as arc knows referenced variable in strongindirect:
strong there no need store autoreleased reference within strongindirect:
, write after phone call - arc standard strong assignment, lines 6-8, , there nil autorelease later (between lines 11 , 12).
does strongindirect:
work strong locals?
of course, here demo3
:
- (void) demo3 { nslog(@"strong local passed strong reference"); breadcrumbs *local; // __strong inferred local = [breadcrumbs newwith:@"apple"]; nslog(@"local: addr %p, contains %p - %@, owners %lu", &local, local, local, [local ownercount]); [self strongindirectwrapper:&local]; nslog(@"local: addr %p, contains %p - %@, owners %lu", &local, local, local, [local ownercount]); }
executing our standard wrapper produces:
1 ark[2041:707] start demo3 2 ark[2041:707] strong local passed strong reference 3 ark[2041:707] >>> 0x100176f30: init 4 ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1 5 ark[2041:707] strongindirect: passed reference 0x7fff5fbfedc0, contains 0x100176f30 - apple, owners 1 6 ark[2041:707] >>> 0x100427d20: init 7 ark[2041:707] >>> 0x100176f30: release 8 ark[2041:707] >>> 0x100176f30: dealloc 9 ark[2041:707] strongindirect: returned 10 ark[2041:707] local: addr 0x7fff5fbfedc0, contains 0x100427d20 - plum, owners 1 11 ark[2041:707] >>> 0x100427d20: release 12 ark[2041:707] >>> 0x100427d20: dealloc 13 ark[2041:707] flush demo3 14 ark[2041:707] end demo3
this same previous example, 2 minor differences:
the address of local on stack passed (0x7fff5fbfedc0
), lines 4 , 5
as stored in local new object cleaned arc, lines 11 , 12
why not add together __strong
reference arguments?
one reason because not strong! arc's pass-by-writeback works weak locals well. our final demo:
- (void) demo4 { nslog(@"weak instance passed autoreleasing reference"); instance = [breadcrumbs newwith:@"peach"]; breadcrumbs __weak *weaklocal = instance; nslog(@"weaklocal: addr %p, contains %p - %@, owners %lu", &weaklocal, weaklocal, weaklocal, [weaklocal ownercount]); [self indirectwrapper:&weaklocal]; nslog(@"weaklocal: addr %p, contains %p -, %@, owners %lu", &weaklocal, weaklocal, weaklocal, [weaklocal ownercount]); }
[here we've used instance
have create weak reference to.]
executing our standard wrapper produces:
1 ark[2041:707] start demo4 2 ark[2041:707] weak instance passed autoreleasing reference 3 ark[2041:707] >>> 0x100427d20: init 4 ark[2041:707] >>> 0x100427d10: release 5 ark[2041:707] >>> 0x100427d10: dealloc 6 ark[2041:707] weaklocal: addr 0x7fff5fbfedd0, contains 0x100427d20 - peach, owners 1 7 ark[2041:707] >>> 0x100427d20: autorelease 8 ark[2041:707] indirect: passed reference 0x7fff5fbfedc8, contains 0x100427d20 - peach, owners 2 9 ark[2041:707] >>> 0x100429040: init 10 ark[2041:707] >>> 0x100429040: autorelease 11 ark[2041:707] indirect: returned 12 ark[2041:707] weaklocal: addr 0x7fff5fbfedd0, contains 0x100429040 -, banana, owners 1 13 ark[2041:707] flush demo4 14 ark[2041:707] >>> 0x100429040: release 15 ark[2041:707] >>> 0x100429040: dealloc 16 ark[2041:707] >>> 0x100427d20: release 17 ark[2041:707] end demo4
notes:
lines 3-5 setting instance
- create new value , release old 1 - real stuff starts @ line 6
arc uses hidden variable (line 8, 0x7fff5fbfedc8
) weak locals (line 6, 0x7fff5fbfedd0
) well
arc has not elided retain/autorelease on assigning hidden variable did above. can see autorelease on line 7 breadcrumbs
missed retain
- ownership of 2 on line 8 shows occurred.
there 2 autoreleases there must 2 corresponding releases when pool drained (lines 14 , 16) - there 1 corresponding dealloc (line 15) other object (0x100427d20
) referenced instance
, arc cleans when our byref
instance goes away.
summary
without added attributes arc right thing local (inferred strong) variables passed parameters reference (inferred autoreleasing). (and "local" includes parameters current method.)
this implemented arc using pass-by-writeback , only works if follow "out" parameter pattern. if wish store passed reference utilize later you'll need more yourself.
if wish pass instance variables reference either need re-create them locals or attribute receiving parameter type __strong
.
pass-by-writeback works __weak
locals.
hope helps.
objective-c automatic-ref-counting
Comments
Post a Comment