Making an instance of a class staying longer in memory (under ARC) - objective-c

I have an instance of a NSObject class that is supposed to parse a XML and save NSManagedObjects, it does everything ok. But I need to receive a NSManagedObjectContextDidSaveNotification inside it to merge CoreData contexts.
The thing is that my instance is being deallocated sooner then I receive the notification above.
How can I prevent my instance being deallocated sooner?
Here's when I call my instance
// in my ViewController implementation
WSNoticia *wsNoticia = [WSNoticia new]; // __strong by default right?
Here's the implementation of WSNoticia:
- (id)init {
self = [super init];
if (self) {
[self parseNews];
}
return self;
}
- (void)dealloc {
// called before mergeChanges: or updateContext:
}
#pragma mark - Private Methods
- (void)parseNews {
// save context in another thread
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setUndoManager:nil]; // performance benefit
[context setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:context];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// fetching things
}];
[blockOperation setCompletionBlock:^{
// updating and saving things
// here the NSManagedObjectContextDidSaveNotification is called (by doing [context save:nil];
}];
// add operation to queue
NSOperationQueue *operationQueue = [NSOperationQueue new];
[operationQueue addOperation:blockOperation];
}
// doesn't get called
- (void)updateContext:(NSNotification *)notification {
NSManagedObjectContext *mainContext = [self managedObjectContext];
[mainContext mergeChangesFromContextDidSaveNotification:notification];
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationNameNoticiasParsed object:self];
}
#pragma mark - NSNotificationCenter
// doesn't get called
- (void)mergeChanges:(NSNotification *)notification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
if ([notification object] == mainContext) {
// main context save, no need to perform the merge
return;
}
[self performSelectorOnMainThread:#selector(updateContext:) withObject:notification waitUntilDone:YES];
}

The strong lifetime qualifier is applied by default, but the lifetime of your variable is just for the method in which you declare it.
If you declare wsNoticia as an instance variable, you should be fine.

Related

How to add notification observers in NSManagedObject subclass to avoid call to unrecognized selector

I've been experiencing a problem with saving managed objects (in a background thread) resulting in calls to unrecognised selectors which seems to be related to the way I'm handling the observation of NSManagedObjectContextObjectsDidChangeNotification. Intermittently it will fail with -[NSFetchRequest myManagedObjectContextDidChange:]: unrecognized selector sent to instance. It's not always a NSFetchRequest, sometimes it's a NSKeyValueObservance or unspecified which makes me believe that the observer is still around after a managed object has been released.
I'm adding and removing the observer to NSManagedObjectContextObjectsDidChangeNotification as seen below. Is there anything wrong with that?
#interface Foo ()
#property (assign, nonatomic, getter = isObserving) BOOL observing;
#end
#implementation Foo
#synthesize observing = _observing;
- (void)awakeFromInsert {
[super awakeFromInsert];
self.addedDate = [NSDate date];
self.modificationDate = [self.addedDate copy];
[self commonAwake];
}
- (void)awakeFromFetch {
[super awakeFromFetch];
[self commonAwake];
}
- (void)awakeFromSnapshotEvents:(NSSnapshotEventType)flags {
[super awakeFromSnapshotEvents:flags];
[self commonAwake];
}
- (void)commonAwake
{
if (self.isObserving) return;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myManagedObjectContextDidChange:)
name:NSManagedObjectContextObjectsDidChangeNotification
object:self.managedObjectContext];
self.observing = YES;
}
- (void)willTurnIntoFault
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext];
self.observing = NO;
[super willTurnIntoFault];
}
- (void)myManagedObjectContextDidChange:(NSNotification*)notification {
NSDictionary *userInfo = [notification userInfo];
NSMutableSet *changedObjects = [NSMutableSet new];
NSSet *objects = nil;
objects = [userInfo objectForKey:NSInsertedObjectsKey];
[changedObjects unionSet:objects];
objects = [userInfo objectForKey:NSUpdatedObjectsKey];
[changedObjects unionSet:objects];
objects = [userInfo objectForKey:NSDeletedObjectsKey];
[changedObjects unionSet:objects];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF IN %#", self.bars];
NSSet *updatedObjects = [changedObjects filteredSetUsingPredicate:predicate];
if (updatedObjects.count > 0) {
NSDate *now = [NSDate date];
if (self.modificationDate == nil || [now timeIntervalSinceDate:self.modificationDate] > 1.0) {
self.modificationDate = now;
}
}
}
#end
Check to see when didTurnIntoFault is called. If it is called, try:
[[NSNotificationCenter defaultCenter] removeObserver:self]
Note that the contextDidChange notification might be called on a different thread then the didTurnIntoFault. And so you might have a race condition. Make sure the adding and removing of the observe is done on the same thread (which should be the one on which the managedOject and hence the managed object itself was created).

Are there any workaround for randomly crash with EXC_BAD_ACCESS on posting NSNotification to the -[NSNotificationCenter addObserverForName:...]

We have on class that is observe the notification with -[NSNotificationCenter addObserverForName:object:queue:usingBlock:] but it is randomly crash with EXC_BAD_ACCESS.
#interface Test: NSObject {
id obs;
}
#end
#implementation Test
- (id)init {
self = [super init];
if (self) {
Test * __weak weakSelf = self;
self->obs = [[NSNotificationCenter defaultCenter] addObserverForName:Notification object:nil queue:nil usingBlock:^(NSNotification *note) {
Test *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[strongSelf handleNotification:note];
}];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self->obs];
self->obs = nil;
}
#end
We found that sometimes when the notification is posted NSNotificationCenter has already get the observer and prepare to call the block for the notification but in the mean time, this object is dealloced and so with the self->obs, which make it crash on [NSNotification postNotification...] method because of the zombie self->obs
Are there any workaround or fix on this issue?
PS. This is ARC code.
Are there any workaround for randomly crash?
Enable zombies - this will cause the exception to breakpoint on the offending line. From there it will be a lot easier to figure out than the general EXC_BAD_ACCESS crash.

iOS: Passing data between views

I have two views which are created programmatically. Let's name them view1 and view2. In view1 there is a picker and a button. The idea is when the user choose value and press the button selected value to be accessable from view2. For this I use NSNotificationCenter. Here is some code.
view1.m
-(void)registerNotification
{
NSDictionary *dict = [NSDictionary dictionaryWithObject:self.selectedOption forKey:#"data"];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"pickerdata"
object:self
userInfo:dict];
}
-(void)loadSecondView
{
self.secondView = [[secondViewController alloc]init];
[self.view addSubview:self.secondView.view];
[self registerNotification];
[self.secondView release];
}
view2.m
-(id)init
{
if(self = [super init])
{
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(reciveNotification:)
name:#"pickerdata" object:nil];
}
return self;
}
-(void)reciveNotification:(NSNotification *)notification
{
if([[notification name] isEqualToString:#"pickerdata"])
{
NSLog(#"%#", [NSString stringWithFormat:#"%#", [[notification userInfo] objectForKey:#"data"]]); // The output of NSLog print selected value
// Here is assignment to ivar
self.selectedValue = [NSString stringWithFormat:#"%#", [[notification userInfo] objectForKey:#"data"]];
}
}
The problem starts here. The logic which is interested of that value is implemented in loadView method. The problems is that loadView is executed before reciveNotification method and selectedValue does not contain needed information yet.
What to do so the information provided from NSNotificationCenter to be accessible from loadView method ?
I don't know if I fully understand your question, but wouldn't it be easier to pass the value directly to the viewController instead of dealing with notifications?
-(void)loadSecondView
{
self.secondView = [[secondViewController alloc]init];
self.secondView.selectedValue = self.selectedOption;
[self.view addSubview:self.secondView.view];
[self.secondView release];
}

Objective C: How to release delegates in this situation

I am using custom delegate objects to do some cleanup tasks after a request finishes. ASIHTTPRequest doesn't retain delegates so I can't autorelease them. Right now this is how I am allocating and releasing the delegates.
App Delegate
MyDelegate *delegate = [[MyDelegate alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:delegate];
MyDelegate.m
- (void)requestFinished:(ASIHTTPRequest *)request
{
[self release];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
[self release];
}
Is there a better way to do this? Having the delegates release themselves seems ugly and Xcode's build and analyze feels uncomfortable with what I'm doing.
A simple approach would be to maintain a mutable set of delgates for each active request in your main controller (the app delegate, in this case):
#interface MyAppController
{
NSMutableSet * activeDelegates;
}
#end
#implementation MyAppController
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
activeDelegates = [[NSMutableSet alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[activeDelegates release];
}
- (void)createRequest
{
MyDelegate *delegate = [[MyDelegate alloc] init];
[activeDelegates addObject:delegate];
[delegate release];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
...
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
MyDelegate *delegate = [request delegate];
[delegate doSomething];
[activeDelegates removeObject:delegate];
{
- (void)requestFailed:(ASIHTTPRequest *)request
{
[activeDelegates removeObject:[request delegate]];
}
#end
Why do you have a separate class purely to be a delegate? That's not how delegate objects typically work. Normally the controller that created the ASIHTTPRequest becomes the delegate, at which point you don't have to worry about releasing it because it will outlive the ASIHTTPRequest already (and if your controller gets dealloced before the ASIHTTPRequest is done, you need to cancel the request).
If You don't want to create a "controller" class for all your delegate instances, i would still at least follow the memory convention rules, and release the delegate immediately after setting it to asihhtprequest. Then i would include a propery in the delegate, something with a name managesItsOwnLifetime (BOOL) and on setting this to YES i would do a [self retain] ...

core data multithread using

Dear community.
When i has a stable version of my application, while i don't start change code to multithread version. What was a difference between previous version:
in (void)applicationDidFinishLaunching:(NSNotification *)aNotification
i do loop to add to queue my code:
NSOperationQueue *opQueueImportUpdateFirstTimeData = [[[NSOperationQueue alloc]init]autorelease];
int i = 0;
for (NSString *carrier in currentCarriers)
{
AppController *operation = [[AppController alloc] initAndUpdateCarrier:carrier identifier:i];
[opQueueImportUpdateFirstTimeData addOperation:operation];
i++;
}
External class have:
- (id)initAndUpdateCarrier:(NSString *)forCarrier
identifier:(NSUInteger)iQuene;
{
[super init];
[self setIdentifierQuene:iQuene];
[self setStartForCarrier:forCarrier];
[self setPercentDone:0.0];
This point is a very important:
[self setDatabase:[[MySQLIXC alloc] init]];
u can't alloc other classes in process of u multithreading, i don't know why, but this is take malloc_error in whole queues
[self setAppDelegate:[[NSApplication sharedApplication] delegate]];
[self setManagedObjectContext:[[NSManagedObjectContext alloc] init]];
return self;
}
And in external class i have:
-(void) main;
{
[self makeUpdatesForCarrier:startForCarrier andTypeOfOperation:#"rates" forDirection:#"incoming"];// mySqlConnector:database];
it's a just some functions which working on local moc.
When i start application, interface didn't place it in background, and start visualization only after all queues will finish.
Before i try to alloc]init] MySQLIXC class inside my external class, but it gives me a lot of malloc_error_break exceptions, like somebody try to freeze memory for me.
Tnx.
Here is moc declaration:
in .h:
#property(retain) NSManagedObjectContext *managedObjectContext;
in .m:
#synthesize managedObjectContext;
Set persistent store coordinator:
[[self managedObjectContext] setUndoManager:nil];
[[self managedObjectContext] setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
Merge changes for with main moc:
- (void)mergeChanges:(NSNotification *)notification;
{
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
And in one place of my code i'm using a main moc (only for read information, i know that moc not thread safe):
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"DestinationsListForSale"
inManagedObjectContext:mainContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"lastUsedProfit > 0"];
[request setPredicate:predicate];
First off, CoreData is NOT thread safe. I would strongly advise that if you do not understand core data in a good deal of detail you keep you application singlethreaded or at the very least ensure that you always access the store from a single thread (probably best to use the main thread).
That said, this will not cause malloc_error_breaks AFAIK. You would see core data merge error exceptions and similar problems.
Could you show the code where you set up the moc further - just allocating and initing a moc is not enough - you have to set it's NSPersistentStoreCoordinator