Singletons and memory management in Cocoa - cocoa

I have a singleton as class method:
+(WordsModel *) defaultModel{
static WordsModel *model = nil;
if (!model) {
model = [[[self alloc] init] autorelease];
}
return model;
}
What happens with the static reference to model inside the method? Will it ever get released?

Not only will it get released (because you sent it an -autorelease message), your next attempt to use it will probably lead to a crash because the model pointer wasn't set to nil when the object was released. So, it will then point to memory that's either garbage, or (if that memory has been re-used) to a different object than the one you expected.

It won't work as you are autoreleasing your instance of your class...
On the next runloop, it will be released...
Take a look at the standard singleton patterns:
http://www.cocoadev.com/index.pl?SingletonDesignPattern
The static instance should be a global variable, that will be freed when your app exits...

Related

Is Caching NSMangagedObject Instances A Bad Idea?

I have a core data entity called Product which is initially populated when the user first logs into the application. It can then be loaded again, if the user requests a refresh.
The Product entity is queried at several places in the app. So, I decided to implement a simple cache that can be shared across the app. The cache keeps the Product NSManagedObjects
in a map. Is this a bad idea?
The ProductCache class:
#interface ProductCache ()
#end
#implementation ProductCache {
}
static NSDictionary *productsDictionary = nil;
static ProductCache *sharedInstance;
+ (ProductCache *)sharedInstance {
#synchronized (self) {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
[sharedInstance reload];
}
}
return sharedInstance;
}
- (void) reload{
NSMutableDictionary *productsMap = [[NSMutableDictionary alloc] init];
CIAppDelegate *delegate = (CIAppDelegate *) [UIApplication sharedApplication].delegate;
NSManagedObjectContext *managedObjectContext = delegate.managedObjectContext;
NSArray *allProducts = [CoreDataManager getProducts:managedObjectContext];
for (Product *product in allProducts) {
[productsMap setObject:product forKey:product.productId];
}
productsDictionary = productsMap;
}
- (NSArray *)allProducts{
return [productsDictionary allValues];
}
- (Product *) productForId:(NSNumber *)productId {
return productId ? [productsDictionary objectForKey:productId] : nil;
}
#end
Personally I would not cache Core Data objects like this. Core Data is your cache. When you also factor in the the issue of threading (NSManagedObject instances cannot cross the thread boundary) it becomes even more risky to put an in memory cache on top of Core Data. This does not even take memory issues into consideration which all things being equal, your cache is not going to perform as well as Apple's cache (i.e. Core Data).
If you need instant access (vs. the nanosecond access on disk) to your Core Data objects then consider copying your on disk cache into an in memory cache and then access that. However, if nanosecond access times are sufficient, leave it in Core Data and fetch the entities when you need them. Build convenience methods for the fetch if you find it repetitive but don't put a cache on top.
I'd like to say it's unnecessary, but I guess it really depends on a few factors.
How large is the Products object?
Is the querying/re-querying extremely CPU intensive?
Does your main thread get blocked?
Personally, I think you could avoid caching and simply re-query the Products object on a background thread whenever you need it.
The better question to ask is: Is it even worthwhile using Core Data for this specific aspect of your project?
Why not store the dictionary in NSUserDefaults (if your has is small)?

Xcode override all methods

I want override all methods of a subclass automatically on xcode, for example I have a class extended of UiViewControler, how I override all methods of UiViewController on xcode to be more or less well:
- (id) init
{
return [super init];
}
My intention with this is to log all methods to see when they are called, then my methods will be more or less well
- (id) init
{
[self log];
return [super init];
}
where log is as follow method:
-(void) log
{
NSLog(#"%#",[(NSString *) (NSArray *) [NSThread callStackSymbols][1] componentsSeparatedByString:#"-["][1]);
}
thanks a lot!
In this case you don't have to do anything. If you don't provide an implementation, then the superclass's implementation will be used.
Edited after the question was edited
If you put the log statement in the superclass's implementation then it doesn't matter what you do with your own initialiser.
Why?
One of the many conventions in Cocoa is that each class has a designated initialiser. All the other designated initialisers then call this initialiser. And when you subclass the class, then you create a new designated initialiser for the new class, and as part of the initialisation - this calls the superclass's designated initialiser.
Which is why you see NSObject subclass initialisers calling [super init], because NSObject's designated initialiser is init.
So, just call your logging method in the designated initialiser of your class, and as long as you follow the above convention, this initialiser will always be called by a subclass, and so your logging method will always be called.

NSKeyedArchiver: distinguishing between different instances of the same class

I'm implementing support for Lion's "Resume" feature in my OS X app.
I have a custom subclass of NSViewController in which I implemented the method
encodeRestorableStateWithCoder: as:
#implementation MyClass (Restoration)
-(void)encodeRestorableStateWithCoder:(NSCoder*)coder {
[coder encodeObject:_dataMember forKey:#"object_key"]; // I get the warning below when this line is executed for the second time
}
- (void)restoreStateWithCoder:(NSCoder *)coder {
_dataMember = [coder decodeObjectForKey:#"object_key"];
}
#end
However, since I have multiple instances of MyClass, different values are saved into the same key ("object_key") and I get the following warning from Cocoa:
NSKeyedArchiver warning: replacing existing value for key
'object_key'; probable duplication of encoding keys in class hierarchy
What is the best practice to overcome this problem?
Edit: I found here that each instance automatically has its own namespace to avoid collisions, so the problem might be in the way I'm manually calling encodeRestorableStateWithCoder to different instances with the same NSCoder object without telling it that these are different instances. However, I still can't figure out how to do that properly.
Thanks in advance!
To overcome this problem, it is possible to create a new NSMutableData where each of which is written by a separate (new) NSKeyArchiver, and store them all in an array which is stored in the original NSCoder object.
Here is an example for encoding the restorable state of subitems. The decoding part can be straight-forward given this code.
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[super encodeRestorableStateWithCoder:coder];
// Encode subitems states:
NSArray* subitems = self.items;
NSMutableArray* states = [NSMutableArray arrayWithCapacity: subitems.count];
for (SubItemClass* item in subitems)
{
NSMutableData* state = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:state];
[item encodeRestorableStateWithCoder:archiver];
[archiver finishEncoding];
[states addObject:state];
}
[coder encodeObject:states forKey:#"subitems"];
}

Getting an exception when using KVO

I am implementing MKMapView based application. In that I am using an observer when we tap on a pin. the observer is code is follows,
[annView  addObserver:self
forKeyPath:#"selected"
options:NSKeyValueObservingOptionNew
context:#"ANSELECTED"];
It is working as expected, but some time it is getting exception 'EXC_BAD_ACCESS'. My log is as follows and it is showing me a leaking memory. Do I need to release the server?. If I ? then where should I release this?
An instance 0x1b21f0 of class MKAnnotationView is being deallocated
while key value observers are still registered with it. Observation
info is being leaked, and may even become mistakenly attached to
some other object. Set a breakpoint on NSKVODeallocateBreak to stop
here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x11e5f0> (
<NSKeyValueObservance 0x1b1da0: Observer: 0x120f70, Key path: selected, Options: <New: YES, Old: NO, Prior: NO> Context: 0x2b588, Property: 0x1acaa0>
It is working as excepted, but some time it is getting exception 'EXC_BAD_ACCESS'. My log is as follows and it is showing me a leaking memory. …
An instance 0x1b21f0 of class MKAnnotationView is being deallocated while key value observers are still registered with it.
That's the opposite of a leak. It is being deallocated; a leak is when an object will never be deallocated.
The problem is that it's being deallocated while something else is still observing it. Anything that's still observing this object may also send it other messages later; when it does, those messages will go to a dead object (causing the crash you saw, which happened after that message) or to a different object.
If the object that is observing the MKAnnotationView is owning it and releasing it, it needs to remove itself as an observer before releasing it. If it does not own it, it probably should.
You have to stop observing the annotation view before you release it:
[annView removeObserver:self forKeyPath:#"selected"];
I know that it's quite old but I see that this code is used a lot on stackoverflow and in other repositories, and here is a solution to the problem.
You should create an NSMutableArray ivar in your view controller class in order to store a reference to your annotations view:
MyMapViewController <MKMapViewDelegate> {
NSMutableArray *annot;
}
Initialize it in your viewDidLoad and in your - (MKAnnotationView *) mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>) annotation
you should add the MKAnnotationView to the mutable array itself right before the annView addObserver code:
if(nil == annView) {
annView = [[MyAnnotationView alloc] initWithAnnotation:myAnnotation reuseIdentifier:identifier];
[annot addObject:annView];
}
[annView addObserver:self
forKeyPath:#"selected"
options:NSKeyValueObservingOptionNew
context:(__bridge void *)(GMAP_ANNOTATION_SELECTED)];
Then, in your viewDidDisappear method you can iterate the array and remove manually all the observers:
//remove observers from annotation
for (MKAnnotationView *anView in annot){
[anView removeObserver:self forKeyPath:#"selected"];
}
[annot removeAllObjects];
[super viewDidDisappear:animated];

How to bind a control to a singleton in Cocoa?

I have a singleton in my FTP app designed to store all of the types of servers that the app can handle, such as FTP or Amazon S3. These types are plugins which are located in the app bundle. Their path is located by applicationWillFinishLoading: and sent to the addServerType: method inside the singleton to be loaded and stored in an NSMutableDictionary.
My question is this:
How do I bind an NSDictionaryController to the dictionary inside the singleton instance? Can it be done in IB, or do I have to do it in code? I need to be able to display the dictionary's keys in an NSPopupButton so the user can select a server type.
Thanks in advance!
SphereCat1
I found / made up the answer to this: I simply override the init method so when it's called from the XIB file, it still returns the singleton. I then provide a method named realInit to do an actual initialization the first time init is called.
Code:
-(id)init
{
#synchronized(self)
{
if (_sharedInstance == nil)
{
_sharedInstance = [[VayprServerTypes alloc] realInit];
}
}
[self release];
return _sharedInstance;
}
-(id)realInit
{
if (self = [super init])
{
serverTypesArray = [[NSMutableArray alloc] init];
}
return self;
}
EDIT: Of course you'll need to define _sharedInstance as a static variable at the top of your class implementation:
static ClassTypeGoesHere *_sharedInstance;
ALSO EDIT: Since you now know for sure that your init method will be called at least once, you can go ahead and replace your normal singleton sharedInstance method with this:
+(ClassTypeGoesHere *)sharedInstance
{
return _sharedInstance;
}
If anyone sees any obvious problems with this design, please let me know!
SphereCat1

Resources