I have a TableViewController which when run, makes an instance of another class and calls json with it eg.
TableViewController;
-(void)viewDidLoad{
JSONClass *jc = [[JSONClass alloc]init];
jc.JSONClassDelegate = (id)self;
[jc view];
}
JSONClass will proceed to retrieve data from the web and once done, will send a delegate method call "JSONClassDataReceived" to tableViewController. Such as,
JSONClass;
-(void)viewDidLoad{
//codes of URL connection goes here...
NSMutableURLRequest *request = [[NSMutableURLRequest new];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:responseData waitUntilDone:YES];
}
- (void)fetchedData:(NSData *)responseData {
NSMutableDictionary *data = [NSJSONSerialization
JSONObjectWithData:responseData
options:NSJSONReadingMutableContainers
error:&error];
if (JSONPromotionsLocationsDelegate && [JSONPromotionsLocationsDelegate respondsToSelector:#selector(JSONPromotionsLocationsDataReceived)]) {
[JSONPromotionsLocationsDelegate JSONPromotionsLocationsDataReceived];
}
}
TableViewController;
- (void)JSONClassDataReceived{
[tableView reloadTable];
}
After which relevant data is populated.
How do I stop JSONClass when back button is pressed on TableViewController before the delegate method JSONClassDataReceived is called on my tableViewController?
I tried
jc.JSONClassDelegate = nil;
jc = nil;
when back button is pressed, but my app crashes because JSONClass has reached JSONClassDelegate and thus cannot find the method - (void)JSONClassDataReceived due to the fact that tableViewController view no longer exist. I have also tried implement dealloc in JSONClass. None seem to work.
- (void)dealloc{
self.view = nil;
}
I have having the error EXC_BAD_ACCESS on the lines,
if (JSONPromotionsLocationsDelegate && [JSONPromotionsLocationsDelegate respondsToSelector:#selector(JSONPromotionsLocationsDataReceived)]) {
[JSONPromotionsLocationsDelegate JSONPromotionsLocationsDataReceived];
}
I seem to have fixed the crash by simply setting my delegate to weak instead of assign.
crash
#property (nonatomic, assign)id <JSONClassDelegate> JSONClassDelegate;
no crash
#property (nonatomic, weak)id <JSONClassDelegate> JSONClassDelegate;
When posting a question about a crash to Stack Overflow, it's helpful to include the crash log or stack trace to empower your peers to help you solve the problem.
The correct way to troubleshoot an EXC_BAD_ACCESS crash is to use the Zombies instrument of Instruments. This will tell you exactly what is causing this problem.
Without that information, we can only guess.
But in your case, I can take an educated guess.
Your delegate is being deallocated at some point, and after that you are attempting to use the deallocated memory. This is most likely because you have not declared the delegate as a weak reference. This is specifically mentioned at several points in the documentation.
First, declare your delegate as a weak property.
#property (nonatomic, weak) id JSONClassDelegate;
Don't forget to synthesize it. Not synthesizing properties can lead to bad things.
Now in the methods where you call your delegate you should make it a strong reference for the duration of the method. This prevents it from being deallocated while you are using it. For example:
__strong id aDelegate = [self JSONClassDelegate];
if ([JSONClassDelegate respondsToSelector:#selector(JSONPromotionsLocationsDataReceived)]) {
[JSONClassDelegate JSONPromotionsLocationsDataReceived];
}
This creates a strong local stack variable that points to your weak reference. It will be retained until it goes out of scope.
First, make sure that before calling your delegate, you do this:
if (self.delegate && [self.delegate respondsToSelector:#selector(JSONClassDataReceived)]) {
[self.delegate JSONClassDataReceived];
}
Other than that, in your JSONClass' - (void)dealloc method, you can stop the web call. If you're using ARC, make sure you don't call [super dealloc]. For the dealloc method to be called, you'll need to keep nulling out your jc object.
Hope this helps.
No, you can't (usefully) "test if an address contains a valid object". Even if you were able to grub around inside the internals of the memory allocation system and determine that your address points to a valid object, that would not necessarily mean that it was the same object that you were previously referring to: the object could have been deallocated and another object created at the same memory address.
Found from here.
Related
I've wrote a class which gets an image from the camera. Its header is as follows:
typedef void(^ImageTakenCallback)(UIImage *image);
#interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
UIImagePickerController *picker;
ImageTakenCallback completionBlock
}
-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;
#end
As you can see, I'm trying to make something like that in client code:
[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
}];
Here is how I've implemented ImageGetter:
-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
completionBlock = [completion copy];
picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.delegate = self;
[view addSubview:picker.view];
}
- (void)imagePickerController:(UIImagePickerController *)picker_
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
[picker.view removeFromSuperview];
picker = nil;
completionBlock(image);
}
The problem is since I'm using ARC, the instance of ImageGetter is deallocated instantly after call for -requestImage..., so the weak delegate of picker becomes nil.
Which are common ways to resolve such a issue?
I can see some ways, however, none of them seems to be quite right:
retain ImageGetter from client code, for example, assign it to a strong property. The problems here are: I wont be able to release it by setting this property to nil right after I get image, because this will mean setting retain count of object to 0 while executing the method of this object. Also, I don't want unnecessary properties (well, it is not a big problem, but nevertheless).
disable ARC for ImageGetter and manually retain at start itself and release after sending image to callback.
make static manager ImageGetterManager, which will have method requestImage..., it will create ImageGetter instances, retain them, redirect the requestImage... call, get callbacks from them and release. That seems the most consistent way, but is not it a bit complex for such a little code?
So how can I build such a class?
You can handle that within the ImageGetter class by creating and releasing a "self-reference".
In a class extension in the implementation file, declare a property
#interface ImageGetter ()
#property (strong, nonatomic) id selfRef;
#end
In requestImageInView:, set self.selfRef = self to prevent deallocation.
In the completion method, set self.selfRef = nil.
Remark: Actually you can manage the retain count even with ARC:
CFRetain((__bridge CFTypeRef)(self)); // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self)); // Decreases the retain count.
But I am not sure if this is considered "good programming" with ARC or not.
Any feedback is welcome!
If this issue is introduced when switching to ARC, I should just go for option 1, and define it as a strong property.
However the behaviour is a bit different than you described for option 1: Setting the property to nil, does NOT mean the object is instantly released, it will just cause a decrement of the retaincount. ARC will handle that fine, the object will be released as soon as all referenced objects have 'released' it.
You can use the following strategy:
ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
[imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];
Inside your ImageGetter implementation class, create a method that you can call inside the block like this.
-(void) releaseCompletionBlock
{
completionBlock = nil;
}
I created DownloadAndParseBook class. It will not autorelesed before it gеt any data or network error.
I used [self release], [self retain]. Is it good approach to use [self release], [self retain]? Is DownloadAndParseBook contain any potential bugs?
#implementation GetBooks
-(void) books
{
for(int i =0; i<10; i++)
{
DownloadAndParseBook *downloadAndParseBook =
[[[DownloadAndParseBook alloc] init]autorelease];
[downloadAndParseBook startLoadingBook];
}
}
#end
#implementation DownloadAndParseBook
- (id)initWithAbook:(int)bookID
{
if(self = [super init])
{
[self retain];
}
return self;
}
- (void)startLoadingBook
{
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self saveResultToDatabase];
[self release];
}
#end
Self retaining is very occasionally an appropriate pattern. It's rare, but sometimes in certain kinds of multi-threaded code its important to make sure that you don't vanish in the middle of processing something. That said, this is not one of those times. I'm having trouble imagining a case where your current approach would be helpful. If someone creates your object and then never calls startLoadingBook, then it leaks. If someone calls startLoadingBook, then your object is retained anyway, because NSURLConnection retains its delegate until it finishes.
That said, I believe much of your problem is coming from the fact that your object model is wrong. Neither GetBooks nor DownloadAndParseBook make sense as classes. What you likely mean is BookManager (something to hold all the books) and BookDownloadController (something to manage the downloading of a single book). The BookManager should keep track of all the current BookDownloadControllers (in an NSSet or NSArray ivar). Each BookDownloadController should keep track of its NSURLConnection (in an ivar). You should not just create connections and have them "hang on themselves" (i.e. self-retain). This feels convenient, but it makes the code very hard to deal with later. You have no way to control how many connections you're making. You have no way to cancel connections. It becomes a mess really quickly.
No it is not a best practice.
Retaining / releasing your object should be done by the "owner" of your object.
For your particular example, the owner of your DownloadAndParseBook object is the object that does the alloc/init. That should be the oen retaining/releasing your DownloadAndParseBook instance.
Best practice here would be alloc/init for DownloadAndParseBook, retain done by the owner, all your download/parse logic, then sending a callback to the owner that all the operations are done (through a delegate for example), at which point, the ower sends a release message to your object.
The question would be: Why does an object require to retain itself? You may want to implement your class like a singleton.
Unlike the other responders I would say that your pattern might work. See also Is calling [self release] allowed to control object lifetime?
There are some other issues in your code however:
In -(void) books I guess you want to send the startLoadingBook message to downloadAndParseBook and not to self
If you create a initWithAbook method it will not be called when you init your book with the standard init method. In the current code above [self retain] will be never called
In your code above bookID will not be saved
I would not use "init" pattern here, but everything in a static function thus the caller can not make mistake with the ownership of the class.
Code:
- (id) initWithId:(int)bookId {
self = [super init];
if (self) {
// save bookId here
}
return self;
}
+ (void) startLoadingBookWithID:(int)bookId {
DownloadAndParseBook* book = [[DownloadAndParseBook alloc] initWithId:bookId];
[NSURLConnection connectionWithRequest:request delegate:book];
}
// release self when it finished the operation
// and document well that its behaviour
If you think well, NSURLConnection itself should work exactly the same way: when you don't release an NSURLConnection when it finished its work, it does it itself. However in the connectionWithRequest it also can not autorelease itself since it has to be alive until the request is served. So the only way it can work is the pattern described above
Never use [self release]. The only possible exception would be in an singleton class/object. The methods release and retain should only be sent by the owner of an object. This usually means, whichever object created the object in question, should also be the one to release it.
I'm posting to a users Facebook wall with code similar to this:
[appDelegate.facebook requestWithGraphPath:#"me/feed"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
If I dismiss the hosting UIViewController before the request completes, I get a crash when the request does actually complete because the delegate has been dealloc'd.
There's a good description of the problem I'm facing here: https://github.com/facebook/facebook-ios-sdk/issues/220
- (void)dealloc {
appDelegate.facebook.sessionDelegate = nil;
[super dealloc];
}
This does not work!
The proper solution is to save the FBRequest object when you call the GraphAPI.
So you will be able to set it's delegate property to nil when your class is being dealloced.
Thus cleaning up your mess and avoid crash caused by the SDK's respondsToSelector method.
First declare a FBRequest property in your class.h:
#property (nonatomic, retain) FBRequest *fbRequest;
Synthesize it in your class.m:
#synthesize fbRequest;
Set it when you call the graph API as so:
fbRequest = [appDelegate.facebook requestWithGraphPath:#"me/feed"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
Set it's delegate propery to nil on your class's dealloc method:
-(void) dealloc
{
fbRequest.delegate = nil;
[fbRequest release];
.
.
.
[super dealloc]
}
I had a solution for that but not very convenient;
I am having 2 separate views in the same view controller, where one of them is the one you request the publish the other the destination view.
You just hide one of the views when request is finished. Or hide the other and display the one while requesting.
For example if you are going to another page after logging in :
- (void)fbDidLogin {
// Do necessary stuff
self.secondView.hidden = YES;
self.view.hidden = NO;
}
The idea is like that, it works but not very convenient.
Create an object (separate from the view controller) that will be the FB delegate and don't deallocate that. You could instantiate it from the same place you instantiate the view controller that calls FB.
Or, if you don't care about the return status, don't use a delegate.
If you look at how Facebook's own code deals with this in Facebook.m they create a list to hold _request objects 'NSMutableSet* _requests;'
and this is alloc/init'd & the requests added as you make calls then they remove them all when cleaning up.
See the lines with ' [_requests addObject:_request];' and look at dealloc where you will see:-
for (FBRequest* _request in _requests)
{
_request.delegate = nil;
[_request removeObserver:self forKeyPath:requestFinishedKeyPath];
}
I used this approach in my own FB code to nil the delegates & it stopped the crashes I was having when exiting while a request was active.
What I did to overcome this problem is to create a wrapping class for FB which I called "FacebookManager". It is a singleton in charge of every FB request done along your application. Being a singleton app-wide, it's life cycle lasts also for the whole app life too.
#protocol
#optional
-(void)fbDidLogin;
-(void)fbDidNotLogin:(BOOL)cancelled;
-(void)fbDidExtendToken:(NSString *)accessToken expiresAt:(NSDate *)expiresAt;
-(void)fbDidLogout;
-(void)fbSessionInvalidated;
-(void)request:(FBRequest *)request didLoad:(id)result;
#end
#interface FacebookManager : Facebook <FBSessionDelegate, FBRequestDelegate>
+(FacebookManager *)sharedFacebookInstance;
-(void)setFacebookDelegate:(id)delegate;
-(void)requestWithGraphPath:(NSString *)fbPath;
Other classes, usually ViewControllers, can be FacebookManager's delegate (or, if needed, you can make an array of delegates when processing concurrent requests). When FacebookManager receives the response from the request, it passes this along to the original class.
Ans since it's the sole delegate for every FB request and never deallocates through your program, even when the original class is dealloc'ed, no errors will arise.
As a bonus to all this, you automatically lose those warning for the methods you need not implement if not desired.
Hope this helps in any way!
i'm a little bit confused with memory management in view controllers.
Lets say i have header file like this:
#interface MyController : UIViewController {
NSMutableArray *data;
}
#property (nonatomic, retain) NSMutableArray *data;
#end
and .m file looks like that:
#implementation MyController
#synthesize data;
- (void)dealloc
{
[self.data release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[self.data release];
self.data = nil;
}
Is that ok from the correct memory management point of view? Will that work after dealloc via Memory Warning? How You do that in your apps?
Thanks for your answers ;)
While the alloc-retain calls balance out in viewDidLoad and viewDidUnload and should prove no problem memory-wise, it would be cleaner to take ownership only once and relinquishing it once rather than twice.
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.data == nil)
self.data = [NSMutableArray array];
}
and
- (void)viewDidUnload
{
[super viewDidUnload];
self.data = nil;
}
You are not guaranteed that viewDidUnload will ever get called. Unlike init/dealloc, which get called in pairs, viewDidUnload is undeterministically called. viewDidUnload is only called if there is a low memory situation and your view is not the active view.
Depending on how your model is created and the implications of it remaining in memory, it may make more sense for you not to get rid of it. An example of this may be that recreating that data may involve an expensive web service call. It therefore would be a bad user experience to have to wait for that data to get recreated. If it must absolutely go, a better strategy may be to cache the data to disk so that you can easily reconstruct it.
viewDidUnload should only contain cleaning up your IBOutlets and flushing easily recreatable data.
These lines from -viewDidUnload both release data:
[self.data release];
self.data = nil;
Since you're using the property setter in the second line, and data is a retained property, the setter will release data. This is an over-release, and it'll cause a crash either right away or later, depending on whether other objects also retain that object. To fix, simply delete the first line and rely on the setter to do the right thing.
The -dealloc method, on the other hand, shouldn't use the setter as it does now. You should change:
[self.data release];
to:
[data release];
data = nil; // this line isn't strictly necessary, but often considered good form
The reasoning here is that it's conceivable that this class could be subclassed, and someone might override the property setter in such a way that it has some side effects that could cause problems when the object is being deallocated. You should access the ivar directly -- notice that I left off the "self." so that we're dealing with the ivar and not the property accessor. (-init and -dealloc are the only places where you have to worry about that; use the property accessors everywhere else.)
I must have misunderstood some of the memory management rules, because when I try to fix a memory leak, the App crashes. Let me show you some code:
calendarRequestLog is a property of type MutableDictionary in a singleton object, that exists as long as the App runs. Here's the declaration in the .h file:
#property (nonatomic, retain, readonly) NSMutableDictionary *calendarRequestLog;
I allocate it with (in init):
calendarRequestLog = [[NSMutableDictionary alloc] init];
I fill it with this (notice the retain, that creates the memory leak):
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
I sometimes access it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
// add delegates
}
I empty it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
for (id <ServerCallDelegate> delegate in delegates) { … }
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
}
Here's the code that crashes when I remove the retain above:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
It crashes because delegates is deallocated but not nil. To be more precise, I get an EXC_BAD_ACCESS Exception.
All these methods may be called in different orders or multiple times.
I cannot figure out, why this happens. I thought, collections are supposed to retain their objects - as this array-object (delegates) is still in the collection, it should not be deallocated. Other code cannot be responsible, I showed you all occurrences of calendarRequestLog.
I appreciate all the help I can get!
#Edit
I think I got it.
I call the crashing method when the delegate gets deallocated, so that I do not call the delegate per accident later.
But: I retain the delegates in my calendarRequestLog, so it cannot get deallocated as long as this doesn't get called:
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
...which in turn, deallocates the delegate and calls the crashing method. As the calendarRequestLog has removed the delegates, but not yet the key, we crash.
Ok, I will solve this differently. Thanks for all the comments - thanks to you, I looked elsewhere!
Did you try retaining when fetching so nobody releases your object while you're using it?
NSMutableArray* delegates = [[calendarRequestLog objectForKey:date] retain];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
[delegates release];
Common practice is the following, because you already retain in the .h file:
//create local instance, then copy that to the class wide var
NSMutableDictionary *_calendarRequestLog = [NSMutableDictionary alloc] init];
self.calendarRequestLog = _calendarRequestLog;
[_calendarRequestLog release];
Also, I don't really understand why you would retain here:
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
Why not just change that to:
[calendarRequestLog setObject:[NSMutableArray arrayWithObject:delegate] forKey:date];
Write instead
calendarRequestLog = [[NSMutableDictionary alloc] init];
this
self.calendarRequestLog = [NSMutableDictionary dictionary];
and try to use property instead ivar