Apple's Developer Reference mentions that a an object is deallocated if there are no strong reference to it. Can this happen if an instance method called from a weak reference is in the middle of execution?
For example, consider the below snippet -
#interface ExampleObject
- doSomething;
#end
#interface StrongCaller
#property ExampleObject *strong;
#end
#implementation StrongCaller
- initWithExampleInstance:(ExampleObject *) example
{
_strong = example;
}
- doSomething
{
....
[strong doSomething];
....
strong = nil;
....
}
#end
#interface WeakCaller
#property (weak) ExampleObject *weak;
#end
#implementation WeakCaller
- initWithExampleInstance:(ExampleObject *) example
{
_weak = example;
}
- doSomething
{
....
[weak doSomething];
....
}
#end
Now, In main thread,
ExampleObject *object = [[ExampleObject alloc] init];
In Thread 1,
[[StrongCaller initWithExampleInstance:object] doSomething];
In Thread2,
[[WeakCaller initWithExampleInstance:object] doSomething];
Assuming that the main thread no longer holds a reference to object, what would happen if strong is set to nil, when [weak doSomething] is executing? Is the object GC'ed in this case?
Normally this problem happens during asynchronously blocks execution where there is impossible to avoid this problem by changing logic.
But if you are sure that you do not want to change logic you can use the same solution in your case. You should modify your method this way
- (void) doSomething
{
Your_Class *pointer = self; //Now this local variable keeps strong reference to self
if(pointer != nil){ // self is not deallocated
... your code here
}
//Here pointer will be deleted and strong reference will be released automatically
}
Related
I initialize a variable (userMarkerLayer) in viewDidLoad. When I use it in that method access is ok. but I set a button and I want to access to it when that button tapped.
#implementation MapViewController
{
NTVectorElementLayer *userMarkerLayer;
}
(void)viewDidLoad {
[super viewDidLoad];
userMarkerLayer = [NTNeshanServices createVectorElementLayer];
[userMarkerLayer addMarker: marker]; // This is ok
}
-(IBAction)showList:(id)sender {
[userMarkerLayer addMarker: marker];
// error:
// Thread 1: EXC_BAD_ACCESS (code=1, address=0x150543c30)
}
cannot access the userMarkerLayer!
When I enable ARC, this problem not happens. but I have to set it no.
Since you're using MRC (ARC disabled), please, read Memory Management Policy.
In your code, you have an ivar (instance variable) ...
#implementation MapViewController {
NTVectorElementLayer *userMarkerLayer;
}
... and you're assigning an object to it ...
userMarkerLayer = [NTNeshanServices createVectorElementLayer];
... but the problem here is that you do not own this object (no alloc, new, copy or mutableCopy in the createVectorElementLayer method name).
It means that you're storing a reference to this object, but this object can go away anytime. You have to use retain to take an ownership of this object.
To fix this, take an ownership ...
userMarkerLayer = [[NTNeshanServices createVectorElementLayer] retain];
... and add dealloc method ...
- (void)dealloc {
[userMarkerLayer release];
[super dealloc];
}
Or declare it as a property with retain ...
#interface MapViewController ()
#property (nonatomic, retain) NTVectorElementLayer *userMarkerLayer;
#end
... and use self.userMarkerLayer instead of just userMarkerLayer ...
self.userMarkerLayer = [NTNeshanServices createVectorElementLayer];
Who can tell me why the following code causes different results when the test parameter and the testMember variable point to the same address
#interface TestClass : NSObject
#property (nonatomic, copy) void (^testBlock)(TestClass *test);
#end
#implementation TestClass
- (void)viewDidLoad {
[super viewDidLoad];
self.testBlock(self);
}
#end
#implementation OtherClass
- (void)viewDidLoad {
[super viewDidLoad];
TestClass *testMember = [[TestClass alloc] init];
// Case 1
testMember.testBlock = ^(TestClass *test) {
NSLog(#"%#",test); // This does not create circular references
};
// Case 2
testMember.testBlock = ^(TestClass *test) {
NSLog(#"%#",testMember); // This creates circular references
};
}
#end
For the case 1, the test variable is just a local function parameter, the block didn't retain any other variables from outside.
For the case 2, the testMember object comes from block outside when defining the block, the block retains its reference count. Since the testBlock is defined as a property of class TestClass, so the testMember owns the testBlock. So, the testMember and testBlock retain each other, that's the circular references.
To fix the circular retain issue of case 2, use this:
__weak TestObject *weakMember = testMember;
testMember.testBlock = ^(TestClass *test) {
__strong TestObject *strongMember = weakMember;
NSLog(#"%#", strongMember);
};
Because the weakMember variable won't increase the reference count of real testMember object, so did the testBlock. When starting executing the testBlock, the strongMember try to retain the weakMember object (which may get nil value depends on your logic design) and get decreased of reference count when finishing the block.
Can doFirst cause a retain cycle here?
#interface Example : NSObject
#property (nonatomic, strong) void (^block)();
#end
#implementation Example
- (void)doFirst
{
__weak id weakSelf = self;
self.block = ^ {
[weakSelf doSecond];
};
self.block();
}
- (void)doSecond
{
self.value = //...
// do other stuff involving self
}
#end
Unlike blocks, methods are not objects; they cannot hold a permanent reference to objects.
Your code would not cause a retain cycle. The fact that the code inside doSecond references self explicitly does not mean that self would get retained an extra time. When your block calls doSecond, its self comes from the weakSelf reference inside doFirst.
Note: When you store blocks as properties, use (nonatomic, copy) instead of (nonatomic, strong).
No It won't. Because It just point to method which won't hold whatwhere inside methods which just an reference as like object.
I am developing an ARC enabled project. From a view controller I am pushing MyClass,
- (void)pushMyClass {
MyClass *myClass = [[MyClass alloc] init];
[self.navigationController pushViewController:myClass animated:YES];
}
After doing some operations I am popping MyClass. The problem here is that MyClass is not getting deallocated. Following is how the classes look.
/* MyHelperClassDelegate */
#protocol MyHelperClassDelegate <NSObject>
- (void)helperDidFinishHelping:(MyHelperClass *)helper;
#end
/* MyHelperClass Interface */
#interface MyHelperClass : NSObject {
__weak id <MyHelperDelegate> delegate;
}
#property(nonatomic, weak) id<MyHelperDelegate> delegate;
- (void)startHelping;
#end
/* MyHelperClass Implementation */
#implementation MyHelperClass
#synthesize delegate;
- (void)dealloc {
delegate = nil;
}
/* MyClass */
#interface MyClass : UIViewController <MyHelperClassDelegate> {
MyHelperClass *helper;
}
#implementation MyClass {
- (void)dealloc {
helper.delegate = nil;
}
- (void)getHelp {
helper = [MyHelperClass new];
helper.delegate = self;
[helper startHelping];
}
- (void)helperDidFinishHelping:(MyHelperClass *)helper {
}
}
MyHelperClass calls a web service using NSMutalbleURLRequest & NSURLConnection to fetch some data and saves it to user defaults.
One thing to notice here is, if I comment the line helper.delegate = self;, then MyClass gets deallocated.
What to do to make MyClass get deallocated when it is popped out of navigation controller?
Thanks.
Your delegate code looks correct (except your use of an ivar, you don't show a #synthesize so you may have _delegate and delegate both). Its quite likely that something else is retaining MyClass. What I suggest you do is add a NSLog to your MyClass dealloc. Then push it, and immediately hit the back button and see if its dealloc'd or not. If not, then take a hard look at what you do in viewDidLoad et al and start commenting out sections of that code until you can get the dealloc.
Also, I assume you don't keep a strong reference in the class that pushes the MyClass object.
I agree with Chuck that one cannot say much from the code provided. But one reason why the MyClass object is not deallocated might be that it is retained by your helper object since delegate is declared as strong, and the MyClass object has the property helper also declared as strong. In this case you had a retain cycle, and none of them can be released.
The trick could possibly lie within the fact that you use NSURLConnection. It is not specified how you use this class with the code that you've provided, but please note the special considerations referenced in the NSURLConnection class reference:
Special Considerations: During the download the connection maintains a
strong reference to the delegate. It releases that strong reference
when the connection finishes loading, fails, or is canceled.
Is it ok to access a delegate property from an Block?
#interface TheObject : NSObject
...
#property (nonatomic, assign) id<SomeDelegate> delegate;
#synthesize delegate
- (void) someMethod {
[someObject doSomethingWithCompletionHandler:^(NSArray *)someArray {
[self.delegate otherMethod:someArray];
}];
}
What happens if the delegate is nilled (from the dealloc method in the object that has also set the delegate) before the completion handler is called?
Could it be a memory bug?
I don't know how to use __block for properties...
Answer from below:
If the delegate is nilled from the object which is the delegate on the dealloc call, everything is fine.
#property (nonatomic, retain) TheObject theObject;
#synthezise theObject = _theObject;
- (void) thatMethod {
self.theObject = [[TheObject alloc] init] autorelease];
_theObject.delegate = self;
}
- (void) dealloc {
_theObject.delegate = nil;
self.theObject = nil;
}
Normally, if your delegate is deallocated before the block is executed, then it would access garbage, since the block is an assign property and the block retains self rather than the delegate since you access it by reference.
However, since you've set it up so that self.delegate gets nilled if delegate is deallocated, you won't have that problem. Instead, if your delegate were deallocated, then in your code you'd just be sending the otherMethod: method to nil, which would do nothing, but also cause no errors.
If you want the method to definitely be sent to your delegate, the solution is to access it by value instead of reference:
- (void)someMethod {
id <SomeDelegate> delegateForBlock = self.delegate;
[someObject doSomethingWithCompletionHandler:^(NSArray *)someArray {
[delegateForBlock otherMethod:someArray];
}];
}
That way delegateForBlock will be a pointer to the same object as self.delegate (at the time you execute someMethod:), and it will be retained.
To find out more about how this works, check out Blocks Programming Topics.
If the delegate is deallocated, you will be accessing a garbage value which will result in EXC_BAD_ACCESS.
You could either do
id <SomeDelegate> dlg = self.delegate
[someObject doSomethingWithCompletionHandler:^(NSArray *)someArray {
[dlg otherMethod:someArray];
}];
or access the ivar directly, so that the block retains it
[someObject doSomethingWithCompletionHandler:^(NSArray *)someArray {
[delegate otherMethod:someArray];
}];