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:.

what right declaration dosomething: method? - (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 hood

instead 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

Popular posts from this blog

How do I check if an insert was successful with MySQLdb in Python? -

delphi - blogger via idHTTP : error 400 bad request -

postgresql - ERROR: operator is not unique: unknown + unknown -