Is calling a method on super supported in the implementation of an Objective-C block?
When I was calling a method on super an EXC_BAD_ACCESS error would be thrown but as soon as I changed those calls from [super methodToCall] to [self methodToCall] and let the message move up the responder chain it worked fine.
There is no implementation of -methodToCall in the instance of the class that the block exists in, but there is one in the superclass (that is, the class that self inherits from).
I'm just curious to learn the details as to why calling a method on super inside the implementation of a block was a problem in the first place (technically) so I can avoid it in the future. I have a suspicion it is related to how the variables are captured in the block and something about the stack and the heap, but I really have no concrete idea.
Note: the block implementation code is called up to a few seconds after the block is stored in a property, the property uses copy so I don't think it's a problem with the block's lifecycle, that all looks to be fine. Also, this was only crashing on the iPhone device (3G) but was working without crashing in the iPhone Simulator.
Results in EXC_BAD_ACCESS:
[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
if (!error) {
[super didRetrieveItems];
} else {
[super errorRetrievingItems];
}
}];
Works perfect, implementations of -didRetrieveItems and -errorRetrievingItems are in the super-class.
[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
if (!error) {
[self didRetrieveItems];
} else {
[self errorRetrievingItems];
}
}];
Technically, this is an issue with the Objective-C runtime, and the underlying mechanics of how calls to super actually work. Basically, they capture both the object which is the recipient of the message (self in all cases) and the class which implements the specific version of the method (the superclass of the class in which the method implementation occurs). Because a lot of the preparation for such a message send happens at compile-time, not runtime, I wouldn't be surprised if it interacted badly with blocks.
I would check to see if self is still valid when the message is about to be sent. Normally, any objects referenced in a block are automatically retained. Since super works a bit differently, it might mean that self is not getting retained like one would expect. One easy way to check this would be to use the calls to super as originally written, and simply leak the object referred to as self, and see if it works. If this turns out to be the problem, you might have to insert a dummy reference to self within the block to get that automatic memory management.
In the strictest sense, however, I'm not sure you can depend on this working forever and always. Although blocks can capture the current runtime state, it doesn't really make sense (from an OOP perspective) for them to break encapsulation and invoke superclass implementations, since the hierarchical level at which methods are implemented should be opaque to any external calling code. I would try to find another solution which doesn't depend on the inheritance hierarchy.
Results in EXC_BAD_ACCESS:
[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
if (!error) {
[super didRetrieveItems];
} else {
[super errorRetrievingItems];
}
}];
Probably due to a bug in the compiler; try adding [self class]; or any other method call to self in that block and it'll probably work.
Works perfect, implementations of -didRetrieveItems and -errorRetrievingItems are in the super-class.
[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
if (!error) {
[self didRetrieveItems];
} else {
[self errorRetrievingItems];
}
}];
I think you may be confused about one of the fundamental aspects of object oriented programming. You say that there are no implementations of those methods in your class, they exist only in the superclass.
Because of inheritance, your class effectively responds to said method calls, too. Just call 'em as you do above using self. It will work find and is exactly how you should be doing it!
Related
There is a similar question here, which doesn't explain exactly what I want: Objective C Blocks as Async-callbacks & BAD ACCESS
I have a view controller, which calls a service with an async callback. The callback is done using a block, which references variables on the view controller to populate them.
It looks like so:
- (void) loadData {
__block MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
me.data = result;
}
}];
}
However, if I dealloc the view controller, 'me' is then badly accessed by the callback.
What is the simplest way of making 'me' NULL? If i put it as an iVar, it then brings back the circular reference... i think?
I think I'm missing something obvious....
Thanks
Are you targeting iOS 5.0 or later (or Mac OS X 10.7 or later)? If so, you can use ARC and a __weak variable (instead of a __block one). This will automatically zero out when the referenced object is deallocated. Your code would look like
- (void)loadData {
__weak MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
MyViewController *strongMe = me; // load __weak var into strong
if (strongMe) {
strongMe.data = result;
}
}
}];
}
If you need support for an older OS then you need to find a different solution. One solution is to just go ahead and let the block retain self. If the service is guaranteed to execute the completion block (and then release it), this will only produce a temporary cycle that will break automatically when the completion block is run. Alternatively if you have some way to cancel the service (in a way that guarantees the block cannot be called after the cancellation), you can stick with the __block and just be sure to cancel the service in your -dealloc. There's other alternatives too but they're more complicated.
I did a combination of things above from the suggestions. Including nilling the blocks. Although, my objects are still not getting released immediately. i.e. I'd put a breakpoint on dealloc of MyViewController, and without the __block variable it would get called at a much later point in time (probably due to the async connection) and sometimes not at all.
The code is fairly complex - so I imagine there are other things going on for it to not work as suggested above.
What I have also done, is used Mike Ash's MAZeroingWeakRef, which i guess is the same as using __weak - which #KevinBallard suggested.
Below is how I've implemented it, and it appears to be working. Dealloc is called immediately on disposal of the view controller, which i want. And I can't get it to crash... and with the log comment that i've put in, I can already see that I'm dodging bullets.
- (void) loadData {
__block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self];
[zeroWeakRef setCleanupBlock: ^(id target) {
[zeroWeakRef autorelease];
}];
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
MyViewController *me = [zeroWeakRef target];
if (!me) {
DULog(#"dodged a bullet");
}
if (!error) {
me.data = result;
}
}];
}
Is there a real retain cycle problem that you're trying to avoid? Is there a reason that self should not simply be retained until -executeWithCompletion: completes? Is there any real chance that it won't complete?
So long as it really will eventually complete (even with failure) and so long as it releases the block after invoking it (perhaps by setting a property to nil), then the retain cycle will eventually be broken and all will be well.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Is calling [self release] allowed to control object lifetime?
What will happen if I use this code snippet?
[self release]
self descrements its own retain count by one, just like release would do with any other object.
The only situation however that I have so far come across, where such a call was appropriate, was the case where an init… method would fail and be expected to return nil and deallocate the just allocated instance.
In 9 out of 10 situations you shouldn't use [self release], I'd estimate.
Something like this (which basically forbids the calling of - (id)init;, for cases where you want to force the use of a particular - (id)initWith… method.):
- (id)init {
[self release];
NSString *reason = [NSString stringWithFormat:#"%# is not a valid initializer for the class %#", NSStringFromSelector(_cmd), NSStringFromClass([self class])];
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:reason
userInfo:nil];
return nil;
}
or this (which basically enforces the proving of an object on initialization)
- (id)initWithFoo:(Foo *)foo {
if (!foo) {//foo is required to be non-nil!
[self release];
return nil;
}
//proceed with initialization
return self;
}
These however are not the only ever occasions where [self release] would be appropriate. But merely the ones I've come across so far. (as Oliver correctly pointed out)
Edit: Self-invalidating NSTimers would probably be another (quite common, yet somewhat special) situation.
You will release yourself, what means that the object declares itself that it does not have to still live. If no one else retains tha object, it dies and its memory is freed
You have little reasons to do that, except if you want to create object that are not controlled by anyone else and are self controlled.
I mean, for example, there is the case where you may want to create an self living object that makes some treatments, and suicide when it has finished without having to tell anyone else it has finished.
Let's imagine for example a class whose purpose is just to send a message to a server :
You instanciate the class, as soon as it is istanciated it send a message to a server, it waits for the server answer, and if the answer is ok, it suicide. That way, you don't have to manage any server answer from within another class controller whose purpose is not to manage such events.
It just decrements the receiver’s reference count.
I must be doing something wrong, but the Automatic Reference Counting docs don't give me a hint on what it might be. What I'm doing is calling a method with a block callback from inside a delegate method. Accessing that same delegate from inside the block results in a bad access. The problem is the object I'm passing - loginController which is sending the message to its delegate - is clearly not released, when I don't access it inside the block I can call the method multiple times without an issue. Here's my code:
- (void)loginViewDidSubmit:(MyLoginViewController *)loginController
{
NSString *user = loginController.usernameLabel.text;
NSString *pass = loginController.passwordLabel.text;
__block MyLoginViewController *theController = loginController;
[self loginUser:user withPassword:pass callback:^(NSString *errorMessage) {
DLog(#"error: %#", errorMessage);
DLog(#"View Controller: %#", theController); // omit this: all good
theController = nil;
}];
}
NSZombieEnabled does not log anything and there is no usable stack trace from gdb. What am I doing wrong here? Thanks for any pointers!
Edit:
I figured the problem has a bigger scope - the callback above is called from an NSURLConnectionDelegate method (the block itself is a strong property for that delegate so ARC should call Block_copy()). Do I need to take special measurements in this scenario?
Flow (the loginController stays visible all the time):
loginController
[delegate loginViewDidSubmit:self];
View Delegate
(method shown above calls the loginUser: method, which does something like:)
httpDelegate.currentCallback = callback;
httpDelegate.currentConnection = // linebreak for readability
[[NSURLConnection alloc] initWithRequest:req
delegate:httpDelegate
startImmediately:YES];
NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)aConnection
didFailWithError:(NSError *)error
{
if (NULL != currentCallback) {
currentCallback([error localizedDescription]);
self.currentCallback = NULL;
}
}
And this is where I get the bad access, but ONLY if I access that loginController variable...
Set copy attribute to the property, or just call 'copy' method for the block.
- (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
{
callback = [callback copy];
The actual solution was that I had the block as a strong property, but it should have been a copy property! D'oh!
First "Solution":
I just found a way to prevent the bad access. As shown in my Edit above, the View Delegate forwards the block to the httpDelegate (an instance of another class), which in turn keeps a strong reference to the block. Assigning the block to a temporary variable and forwarding the temporary block variable solves the problem, for whatever reason. So:
This crashes on block execution, as described
httpDelegate.currentCallback = callback;
This works
MyCallbackType aCallback = callback;
httpDelegate.currentCallback = aCallback;
I'll accept this as the answer, if anybody has more insights I'm happy to revise my decision. :)
I figure what is happening there is that the loginController is dead right after calling its delegate. Therefore a crash occurs. Without more information I can think of possible scenarios only:
The block do not retains the loginController object (__block type modifier). If the block is executed asynchronously, the loginController might no longer be available if it was killed elsewere. Therefore, no matter what you want to do with it, you wont be able to access it inside the block and the app will crash. This could happen if the controller is killed after sending loginViewDidSubmit.
I think most likely this could be your situation: The loginController calls its delegate object. The delegate method ends up synchronously invoking the callback block that kills the controller. The controller is expected to be alive after invoking the delegate method. Killing it inside the delegate method, most likely will cause crashes to happen. To make sure this is the problem, simply nil the loginController in the delegate method and put an NSLog statement in the controller after calling the delegate, never mind the block, you will get a crash there.
Perhaps if you paste some code we could help more.
My best.
I am implementing an object reuse scheme using a singleton class.
What I do basically is:
MyClass* obj = [[MyClassBank sharedBank] getReusableItem];
The bank is just an NSMutableSet tweaked for optimum reusability. When I was happily implementing this Singleton, I had in mind that I will just do the following when I am done with "obj":
[[MyClassBank sharedBank] doneWithItem:obj];
Currently, My code would work if I where to use it this way, but I later realized that I sometimes add "obj" to an "NSCollection", and sometimes I call:
[theCollection removeAllObjects];
At first I thought about making my own class that is composed of a collection, then I would iterate the objects within the collection and call:
[[MyClassBank sharedBank] doneWithItem:obj];
But, that's too much of a hassle, isn't?
A neat idea (I think) popped into my mind, which is to override: -(oneway void)release;, so, I immediately jumped to Apple's documentation, but got stuck with the following:
You would only implement this method to define your own reference-counting scheme. Such implementations should not invoke the inherited method; that is, they should not include a release message to super.
Ao, I was reluctant to do that idea .. basically:
-(oneway void)release{
if ([self retainCount] == 1) {
//This will increment retain count by adding self to the collection.
[[MyClassBank sharedBank] doneWithItem:self];
}
[super release];
}
Is it safe to do that?
PS: Sorry for the long post, I want the whole idea to be clear..
EDIT:
How about overriding alloc alltogther and adding [[MyClassBank sharedBank] getReusableItem]; there?
Suggested method:
You're playing with the reference counting system. 99.9999999999999999% of the time this is a bad idea. I would highly recommend going with a different mechanism. Perhaps these objects could implement their own reference count that's independent of the retainCount? Then you could use that referenceCount to actually control when an object is ready to be re-used or not.
Not suggested method:
If, for some weird reason, you can't do that, then you could do the following thing that is still a bad idea and that i don't recommend you actually use:
You can override dealloc:
- (void)dealloc {
[ivar release], ivar = nil;
[anotherIvar release], anotherIvar = nil;
somePrimitive = 0;
// do not call [super dealloc]
}
- (void)_reallyDealloc {
[self dealloc]; // clean up any ivars declared at this level
[super dealloc]; // then continue on up the chain
}
Basically, the dealloc method would be the point at which the object is ready for re-use. When you're totally done with the object and finally want it to go away, you can use the _reallyDealloc method to continue on up the chain, eventually resulting in the object getting freed.
PLEASE don't do this. With things like Automatic Reference Counting, this is going to introduce you into a world of hurt and really bizarre debugging scenarios. A lot of the tools and classes and stuff depend on the reference counting mechanism to be working without alteration, so screwing around with it is usually not a Good Idea™.
For ppl who find this approach interesting/useful, Here is a cleaner way than calling [super dealloc]; directly (which is definitely bad)
//BAD!
//-(void)dealloc{
// for some reason, the retainCount at this point == 1
// if (![[BankStep sharedBank] purgeFlag]) {
// [self resetObject];
// [[BankStep sharedBank] doneWithItem:self];
// } else {
// [children release];
// [super dealloc];
// }
//}
by calling [[Bank sharedBank] purgeBank]; , set the flag to true, then remove all objects from the NSSet.
Adapted solution:
#Joe Osborn idea of using categories to implement a returnToBank Method!
Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.