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];
}];
Related
I have a problem understanding the Objective-C and the ARC.
As I understood the strong pointers will be dealloced automatically for you, so you don't have to think about it (dealloced in dealloc method, or after the last time the object was used ?).
So I wrote a little app, with 2 viewControllers and a NavigationController, which enters one view and then goes back.
The dealloc method was called, but the property, which I set at viewDidLoad method, wasn't deallocated, it is still pointing to some object.
Code:
The first viewController has a button, which by pressing it, performs a segue to another viewController. No code there.
SecondViewController.m
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
}
- (void)dealloc {
NSLog(#"%#", _myString);
// and now it is deallocating the _myString property ???
}
#end
Then, I tried to do another thing.
The idea was to create a weak pointer, which points to the same memory address, as the strong pointer. I though, that the weak pointer should be nil in any case.
Since the dealloc method is called, all weak pointers should be niled
Since the strong pointer was used only in viewDidLoad, it should be deallocated way before the dealloc method.
The problem is, it is not deallocated.
Why ?
Code for secondViewController:
#interface SecondViewController ()
#property (nonatomic, strong) NSString *myString;
#property (nonatomic, weak) NSString *test;
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", _myString);
_myString = #"it works";
_test = _myString;
}
- (void)dealloc
{
NSLog(#"%#", _test);
}
#end
Deallocation of the properties happens at the end of the dealloc method. If you overwrite the dealloc method, the properties won't yet be deallocated inside that method.
You could test this by creating a weak property in your first view controller, assign the strong property of the second view controller, then log the value of it when the application returns to the first view controller.
The simplest way to illustrate weak references is with the following example...
Given the following two classes:
#interface ObjectWithStrongRef : NSObject
#property (strong) NSString *ref;
#end
#interface ObjectWithWeakRef : NSObject
#property (weak) NSString *ref;
#end
We will create an instance of ObjectWithWeakRef with a scope larger than that of ObjectWithStrongRef, assign the latter's ref property a value, then have the former's ref point to this same object, then we will check ref in both scopes.
int main(int argc, const char * argv[]) {
ObjectWithWeakRef *weak = [[ObjectWithWeakRef alloc] init];
#autoreleasepool {
ObjectWithStrongRef *strong = [[ObjectWithStrongRef alloc] init];
strong.ref = [NSString stringWithFormat:#"Hello %#", #"World"];
weak.ref = strong.ref;
NSLog(#"Weak.Ref = %#", weak.ref);
}
NSLog(#"Weak.Ref = %#", weak.ref);
}
Note, that we can't simply assign ref to a literal string. Objective-C tends to keep these around in memory so it can do some memory optimizations, but when we use stringWithFormat:, it'll create an autoreleasing string.
At the first NSLog statement, strong.ref maintains a strong reference to the string object, so when we log weak.ref, the object is not yet deallocated, so it correctly logs "Hello World".
Between the first and second NSLog call, we've exited the #autoreleasepool, within which the strong object was scoped (if we put an NSLog message in ObjectWithStrongRef's dealloc, we'd see it called here). Because strong has deallocated as we exit the #autoreleasepool, there are no longer any strong references to the string object we have references to--we only have weak's weak reference to the memory, so the string object also deallocates (just after strong has deallocated).
So in the second NSLog call, we'll see Weak.Ref = (null) printed.
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.
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
}
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.
How do I keep an object in scope, which performs a task in background, that was instantiated from a method of another object?
See the following example. The display method of object A instantiates object B and calls its display method. When the B object's display method, starts an asynchronous task, control goes back to A::display method, which returns, and bObject gets deallocated. When bObject's data/method are accessed in the background thread, this will throw exception.
What is the best way of maintaining the scope for object B? I can declare the object B as a member variable for A, but this will increase the scope for the entire lifespan of object A.
#implementation A
-(void) display
{
B* bObject = [[B alloc] init];
[bObject display];
}
#end
#implementation B
-(void) display
{
dispatch_async((dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0),{
self.data = 5;
---------
});
#end
Previously in iOS you would be able to do this and then assuming your object created returned with some delegate callback you could release it. Now you will have to make "bObject" a member variable for your class and either set it to nil when you are done with it or wait for A to be destroyed which will put bObject out of scope. I'm assuming this is with ARC and as soon as "display" finishes your local variable is out of scope and is deallocated because of ARC.
you can add this to A:
#property (nonatomic, strong)B *bObject;
You can create an iVar B *bObject in A's interface. Just make sure you are not releasing bObject anywhere. Also, you may want to not reference self from the block in bObject.
#interface A : NSObject
#property (strong, nonatomic) B *bObject;
#end
#implementation A
- (void)display {
self.bObject = [[B alloc] init];
[self.bObject display];
}
#end
#implementation A
- (void)display {
B *safeSelf = __block self;
dispatch_async((dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0),{
safeSelf.data = 5;
----
});
}
#end