Is "self" weak within a method in ARC?
Asked Answered
A

2

6

I have a method that occasionally crashes.

-(void)foo{
    [self doSomething];
    [self.delegate didFinish];
    [self doSomethingElse];
}

-doSomething works correctly, then I call to a delegate -didFinish. Within -didFinish, the reference to this object might be set to nil, releasing it under ARC. When the method crashes, it does so on -doSomethingElse. My assumption was that self would be strong within a method, allowing the function to complete. Is self weak or strong? Is there documentation on this? What would the reasoning be for it being strong or weak?

Edit

Upon being inspired by some of the answers below, I did some investigation. The actual cause of the crash in my case was that the NSNotificationCenter does not retain the observer in any case. Mike Weller indicates below that callers of methods should retain the object while it is being called in order to prevent the case that I described above, however it appears that NSNotificationCenter ignores this issue, and always maintains a weak reference to the observer. In other words:

-(void)setupNotification{
    //observer is weakly referenced when added to NSNotificationCenter
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotification:)
                                                 name:SomeNotification object:nil];
}

//handle the notification
-(void)handleNotification:(id)notification{
    //owner has reference so this is fine
    [self doSomething];
    //call back to the owner/delegate, owner sets reference to nil
    [self.delegate didFinish];
    //object has been dealloc'ed, crash
    [self doSomethingElse];
}
Archon answered 1/8, 2013 at 14:13 Comment(4)
If it is weak then self should become nil, and sending message to nil won't crash. So I would guess, neither.Dusa
Did you use NSLog to see what's up with self.delegate?Caviness
You should post some more code and your stack trace, also whatever exception or error is occurring. How is the delegate declared?Polygamy
I am not interested in debugging the code as it has been fixed already. I am interested in knowing the answer to this question similar to what Mike provided below.Archon
S
10

self would be strong within a method, allowing the function to complete. Is self weak or strong?

self is neither strong nor weak in ARC. It is assumed that the caller holds a reference, and self is unsafe unretained.

It's also true that self can be -dealloced within its own method under ARC, and it's regarded as "Undefined Behavior (or at least dangerous)" for your program to do this.

What would the reasoning be for it being strong or weak?

It's unretained for Performance -- to avoid what is (in the vast majority of cases) an unnecessary reference count inc/dec. Even if they did all those extra ref count operations, your program would still be susceptible to such problems in multithreaded programs or in the presence of a race condition (also UB). So this is one of those extreme edge cases they (rightly) determined they need not defend themselves from.

Is there documentation on this?

Of course! :)

Savannasavannah answered 2/8, 2013 at 7:52 Comment(8)
"and it's regarded as "Undefined Behavior (or at least dangerous)" for your program to do this." You're saying, if bad things happen to happen in your program, then it's undefined behavior. Well of course, but that is not useful. How would the programmer know that this could happen?Marja
@Marja that's a quote from the linked docs. the purpose of the statement is clear (although i'd have omitted the "at least dangerous" bit). this doesn't happen by chance. it would seem by chance until the issue is identified. the purpose of the statement is: when/if your program allows this to happen, regard it as undefined behavior. undefined behavior has a very specific definition in C langs. so the statement serves to specify the severity of this error (if encountered). a programmer who understands their program could identify it during development. crashes and zombies would help, too.Savannasavannah
An object gets deallocated when it no longer has any strong references. The problem is, any method call could potentially cause the last strong reference to the current object to be removed. So under that definition, any method that contains a method call is undefined behavior (unless proven otherwise)? That would make "defined behavior" pretty meaningless.Marja
@Marja that's quite an imagination you have -- by your logic, even using memory returned from malloc should be UB. no. objective-c is not memory safe; it's a superset of c. ARC is not a garbage collector. judging by your SO account, you should already know these things.Savannasavannah
No, I never said it was memory safe. However, if you follow a correct way to write things then it should be safe, no? I am saying that the fact that self is not strong makes it possible for code which is written using correct Objective-C techniques and ARC, to still inadvertently cause self to be deallocated in the middle of a method, in a way where you cannot pinpoint any particular part of the code as "wrong".Marja
@Marja i happen to agree with their rule used in ARC. in C, C++, and ObjC etc -- using a freed/deleted object is UB and the compiler/runtime won't protect you from this. self being released within a method is a corner case, and will often be apparent if one understands their program's flow. it would also result in many more ref count ops. to make ARC programs highly safe would require much more than we have today. had ARC aimed for this, i expect it would have been a good amount slower than MRC and they should probably just drop interoperability with MRC binaries. (cont)Savannasavannah
(cont) making this specific form safe would have a high cost and would help a very low % of cases. i consider ARC superior to their attempt at ObjC-GC, and i think they have a good balance of safety/speed/compatibility/expectations. so i think the way it is today was a good way to introduce ARC, considering all the code that relies on MRC -- a practical compromise which is compatible with much that is out there. (cont)Savannasavannah
(cont) now, if i were to put on a language designer hat and approach the problem how managed memory should be, then i agree with you in that self should not be allowed to be deallocated inside a method. however, if i were to create an implementation to support the safe managed model, i just wouldn't bother maintaining binary compatibility with MRC programs. that's not a light decision to put on decades of code -- i was there for ObjC-GC binary incompatibility; it was a PITA.Savannasavannah
I
5

self is neither weak nor strong. If you can access self then you are in the scope of a method call, and that method call is being performed by somebody via a reference they must own. self is implied to be a valid reference as long as it's in scope, and it is implied that any memory management or ownership is handled by the caller.

When calling a method through a weak reference, ARC will retain the object for the duration of that method call (see this answer). With strict compiler warnings enabled, you will actually be forced to create a strong reference before sending any methods to that reference.

So by definition, if a method is being called on an object, the caller must already have ownership and nothing needs to be done.

Of course, it is possible to end up calling methods on deallocated objects but that is the result of bad caller code.

Ingress answered 1/8, 2013 at 14:29 Comment(2)
"and that method call is being performed by somebody via a reference they must own. self is implied to be a valid reference as long as it's in scope, and it is implied that any memory management or ownership is handled by the caller." However, something you do within this method could inadvertently break the original ownership relation. It is impossible to guarantee in general that self will still point to a valid object in the middle of the method.Marja
"So by definition, if a method is being called on an object, the caller must already have ownership and nothing needs to be done." Not necessarily. It is only guaranteed that the receiver is valid at the time the call starts; there is no guarantee that it remains valid for the duration of the call.Marja

© 2022 - 2024 — McMap. All rights reserved.