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.
Related
I have a method called in various places called "cancelAllPendingDownloads"
This is a general method that cancels various jobs and updates internal counters.
Problem happens when it is called within the dealloc method
-(void)dealloc
{
[self cancelAllPendingDownloads]; // want to cancel some jobs
}
-(void)cancelAllPendingDownloads // updates some internals
{
__weak __typeof__(self) weakSelf = self; // This line gets a EXC_BAD_INSTRUCTION error in runtime
for(Download *dl in self.downloads)
{
dl.completionHandler = ^{ // want to replace the previous block
weakSelf.dlcounter--;
}
[dl cancel];
}
}
Not sure why it fails in the dealloc method as "self" still exists
When I change the code to
__typeof__(self) strongSelf = self; //everything works fine
__weak __typeof__(self) weakSelf = strongSelf; (or "self") BAD_INSTRUCTION error
The error happens on the second line
Just to make the "you are not supposed" or "You can't" part of the other good answers
more precise:
The runtime function for storing a weak reference is objc_storeWeak(), and the
Clang/ARC documentation states:
id objc_storeWeak(id *object, id value);
...
If value is a null pointer or the object to which it points has begun
deallocation, object is assigned null and unregistered as a __weak
object. Otherwise, object is registered as a __weak object or has its
registration updated to point to value.
Since the self object has already begun deallocation, weakSelf should be set to NULL
(and therefore is not of any use).
However, there seems to be a bug (as discussed here http://www.cocoabuilder.com/archive/cocoa/312530-cannot-form-weak-reference-to.html)
that objc_storeWeak() crashes in this case, instead of returning NULL.
If an object is in dealloc state, you are not supposed to create any new references to it. Consider the object as already destroyed. Don't use it in a callback/delegate any more.
Note that dlcounter won't ever be read. Just cancel the connections without reading the results.
TL;DR
- How can I reference __weak self in dealloc method?
- Don't reference it.
You can't initialize a week (or a strong) reference to self in the dealloc method and use it elsewhere - it's too late, the object will be inevitably destroyed.
However, you might try this:
-(void)dealloc
{
NSArray* localDownloads = self.downloads;
for(Download* dl in localDownloads) {
[dl cancel];
}
}
It should be clear that there are better places to invoke cancellation, for example, in a view controller, you may override viewWillDisappear:.
I am assuming you are using ARC for your project.
Straight from Apple:
Apple Talked about Weak and Strong
__strong is the default. An object remains “alive” as long as
there is a strong pointer to it.
__weak specifies a reference that does not keep the referenced object alive.
A weak reference is set to nil when there are no strong references to the object.
This is an Article Explaining Dealloc:
Dealloc Method Explained and More
This method will be called after the final release of the object
but before it is deallocated or any of its instance variables are destroyed.
The superclass’s implementation of dealloc will be called automatically when
the method returns.
After this being pointed out... I highly recommend you revise your code design because there is no reason for you to call a weak typeof(self) to solve your problem of cancelling those downloads at dealloc or any type of deallocing that involves _weak_typeof__self for that matter.
What I can recommend though is that that class that you are trying to cancel those downloads frin, make it keep track of those downloads with a Download UniqueID and just stop them or delete them at dealloc. Its simpler and easier to manage rather than that wierd call to __weak self and all that code you are doing.
In short: you can use a __strong reference to self in dealloc instead of __weak for your purposes but if and only if that strong reference won't outlive the end of dealloc. Otherwise, I would advise using __unsafe_unretained, which is still unsafe if it outlives the dealloc but is clearer to read.
Longer: I had a similar situation where the object (view controller) during dealloc should unsubscribe from notifications. That's a custom notifications system and unsubscribing requires creating an object with a reference to the entity that's being unsubscribed.
I ended up with the same situation: in dealloc there's no way to create that object because it required a weak reference which caused a crash (here's some stupid demo code, not something you would have in production):
#interface Dummy : NSObject
#property(nonatomic, weak) id weakProperty;
#property(nonatomic, strong) id strongProperty;
#property(nonatomic, unsafe_unretained) id unsafeProperty;
- (instancetype)initWithWeakStuff:(id)stuff;
- (instancetype)initWithStrongStuff:(id)stuff;
- (instancetype)initWithUnsafeStuff:(id)stuff;
#end
#implementation Dummy
- (instancetype)initWithWeakStuff:(id)stuff {
self = [super init];
if (self) {
_weakProperty = stuff;
}
return self;
}
- (instancetype)initWithStrongStuff:(id)stuff {
self = [super init];
if (self) {
_strongProperty = stuff;
}
return self;
}
- (instancetype)initWithUnsafeStuff:(id)stuff {
self = [super init];
if (self) {
_unsafeProperty = stuff;
}
return self;
}
- (void)dealloc {
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"some notification"
object:dummy]; // do something with it
}
#end
If, on the other hand, the reference was strong, all seems to work well (during dealloc). The problem would arise if that newly created object would outlive self:
- (void)dealloc {
Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:#"some notification"
object:dummy]; // do something with it
}); //Crash at the end of the block during dummy's dealloc
}
This would mean that whenever the dummy object would need to dealloc it would try to decrease the ref count of its strongProperty. And at that point the ViewController has been deallocated and released already.
However, IMHO the "safest" way to proceed is to use unsafe_unretained in this case. Technically it's the same as using assign: pointer will be assigned regardless of memory management and that reference will not need to be released when it goes out of scope. But using unsafe_unretained tells the readers of your code (or future you) that you were aware of the risk and there must have been a reason to do what you did.
I have a shared instance (a simple data controller) and in my project I don't use ARC.
static ECOMDataController *sharedInstanse;
#implementation ECOMDataController
+(ECOMDataController *)sharedInstance
{
return sharedInstanse;
}
-(id)init
{
[self checkAndCreateDataFileIfExist];
[self readAppFile];
if (sharedInstanse)
NSLog(#"The shared instance was created already.");
sharedInstanse = self;
return self;
}
And I use it in the other methods like this:
- (void)viewDidLoad
{
[super viewDidLoad];
dataController = [ECOMDataController sharedInstance];
[dataController readAppFile];
[[self tableView] reloadData];
}
As I can see from the leaks instrument - I have a memory leak here - what should I do to release the data controller? And where is better to do that?
Rocky is right: you wouldn't deallocate a singleton. Frankly, I wouldn't use that pattern at all--except for system calls like AppDelegate or NSNotificationCenter. There are a lot of pitfalls with the pattern...but that's my opinion (though I'm not alone in it).
More importantly, why are you not using ARC? There's absolutely no reason not to, and many reasons for it. Especially for a newer developer, there's no sense in fussing about memory management when the compiler will do it for you, anyway--and will do a better job of it. You have enough to learn without fussing over retain counts!
I have my main app delegate
I have a few UIViewController derived instances driven by a Storyboard
Say I'd like to provide a centralized persistence layer for my application - perhaps Core Data of SQLite. Where would I put those objects? I'm missing some centrally accessible "Application" class you can access from all the UIViewController instances.
Is there a pattern to follow here?
you should check the singleton pattern:
In software engineering, the singleton pattern is a design pattern
that restricts the instantiation of a class to one object. This is
useful when exactly one object is needed to coordinate actions across
the system. The concept is sometimes generalized to systems that
operate more efficiently when only one object exists, or that restrict
the instantiation to a certain number of objects. The term comes from
the mathematical concept of a singleton.
here is a source for a example implementation: What should my Objective-C singleton look like?
and here is the direct link for the modern solution:
https://stackoverflow.com/a/145395/644629
What you're describing is your model layer. There are two main ways to manage the model:
At application startup, create the main model object and hand it to the first view controller.
Make the main model object a Singleton.
The "main model object" in both cases is generally some kind of object manager. It could be a document, or it could be a PersonManager if you have a bunch of Person objects. This object will vend model objects from your persistence store (generally Core Data).
The advantage of a Singleton here is that it's a little easier to implement and you don't have to pass around the manager. The advantage of a non-Singleton is that it's easier to have more than one (for a document-based system), and it's easier to test and reason about non-singletons than singletons. That said, probably 80% of my projects use a singleton model manager.
As a side note, that you appear to already understand: never store the model in the application delegate, and never use the application delegate as a "rendezvous point" to get to the model. That is, never have a sharedModel method on the application delegate. If you find yourself calling [[UIApplication sharedApplication] delegate] anywhere in your code, you're almost always doing something wrong. Hanging data on the application delegate makes code reuse extremely difficult.
Go with a singleton pattern, which has scope of application lifetime.
#interface DataManager ()
#end
#pragma mark -
#implementation DataManager
#pragma mark - Shared Instance
static DataManager* sharedInstance = nil;
#pragma mark - Singleton Methods
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (DataManager*)sharedInstance
{
#synchronized([DataManager class])
{
if (!sharedInstance) {
//[[self alloc] init];
sharedInstance = [[DataManager alloc] init];
}
return sharedInstance;
}
return nil;
}
+ (id)alloc
{
#synchronized([DataManager class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a second instance \
of a singleton.");
sharedInstance = [super alloc];
return sharedInstance;
}
return nil;
}
#end
Declare your properties in .h file and synthesize them here in .m file.
To use that property just call:
// set value
[[DataManager sharedInstance] setSharedProperty:#"ABC"]; // If its a string
// get Value
NSLog(#"value : %#", [[DataManager sharedInstance] sharedProperty]);
Hope this is what you required.
Enjoy Coding :)
I have a method that will dismiss the current view in the navigation controller and replace it with another view. The code looks like this
-(void)didTransferRequest:(NSString *)_transferComments {
AddRequestViewController *ar = [[AddRequestViewController alloc]
initAsTransferForRequestID:requestID
withClosingComments: _transferComments]];
UINavigationController *currentNav = self.navigationController;
[[self retain] autorelease];
[currentNav popViewControllerAnimated:NO];
[currentNav pushViewController:ar animated:NO];
[ar release];
}
[AddRequestViewController.m]
-(AddRequestViewController *)initAsTransferForRequestID:(int)requestID
withClosingComments:(NSString *)closingComments{
self = [self initWithStyle: UITableViewStyleGrouped];
if (self) {
_requestID = requestID;
_closingComments = closingComments;
}
return self;
}
The problem is that once the new view is pushed onto the nav stack, it crashes when the view attempts to access the contents passed in by _transferComments. The pointer is pointing to something else which would make sense since the view gets popped.
I was successful in using withTransferComments: [_transferComments copy] but the Analyzer identified a memory leak with this.
Is using copy safe and should I ignore the leak message or is there a better way to send the string over?
Your AddRequestViewController isn't taking ownership of _transferComments.
You need to read Cocoa Core Competencies - Memory Management and Basic Memory Management Rules.
In the code snippet you posted (without the copy), I deduce that AddRequestViewController doesn't send retain to _transferComments. If it wants to make sure _transferComments stays around, it needs to send it the retain message to take ownership of the string. When AddRequestViewController is done with the string, it needs to send it release to relinquish ownership. You would probably do this in -[AddRequestViewController dealloc].
Basically, your initAsTransferForRequestID:withClosingComments: method should look something like this:
- (id)initAsTransferForRequestID:(int)requestId withClosingComments:(NSString *)transferComments {
if (!(self = [super init]))
return nil;
_requestId = requestId;
_transferComments = [transferComments retain];
return self;
}
(Note that I'm using the common convention of naming instance variables with a leading underscore.) Your dealloc method should look like this:
- (void)dealloc {
[_transferComments release];
[super dealloc];
}
When you changed your code to copy _transferRequest, you did create a memory leak. The copy message creates an "owning" reference to the copy, and something needs to take responsibility for relinquishing that ownership. You didn't change either of your objects to do that.
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.)