Problem with value of SIngleton in iPhone app - iphone

Hi have a problem with singleton Class in iPhone app.
I have create a simple class for visualization of NSString value.
My problem is generate when i try to stamp a NSString in a textVIew.
I call my methods and the value of string in Singleton class is (invalid) (i have tested it with debug).
Can you help me with the code solution.
my code:
#import "UntitledViewController.h"
#import "SingletonController.h"
#implementation UntitledViewController
#synthesize resetTextEvent;
#synthesize buttonSavePosition;
-(IBAction)stamp{
textEvent.text = [sharedController name];
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedController = [SingletonController sharedSingletonController];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
#import "SingletonController.h"
#implementation SingletonController
#synthesize name;
static SingletonController *sharedController = nil;
+(SingletonController*)sharedSingletonController{
if(sharedController == nil){
sharedController = [[super allocWithZone:NULL] init];
}
return sharedController;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedSingletonController] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
-(id)init{
self = [super init];
if (self != nil) {
name = [NSString stringWithFormat:#"hello"];
}
return self;
}
-(void) dealloc {
[super dealloc];
}
#end

This line:
name = [NSString stringWithFormat:#"hello"];
is problematic. name refers to an instance variable, not your property. So what's happening is your string is being assigned to name, but it's an autoreleased object. So, at some point in the future, name is released automatically and refers to deallocated memory.
If you've specified the name property as either retain or copy, then either of the following lines will property retain the object:
self.name = [NSString stringWithFormat:#"hello"];
name = [[NSString stringWithFormat:#"hello"] retain];

Related

Why cant I pass this variable from one class to another

I am stuck and need some help understanding why this is not working.
I want to be able to download the HTML of a page and then format it to show correctly, the code inside the second class (spriing) will download and display the HTML in a UITextView if it is placed inside the ViewController, however this is breaking the MVC right?
So could anyone tell me why I am getting the out of scope error on the mStringData variable?
My classes are below:
I have one class which is a view controller;
//Class for the download and processing of data from website
#import "FirstViewController.h"
#implementation FirstViewController
// The designated initializer. Override to perform setup that is required before the view is loaded.
//- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
// if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// // Custom initialization
//}
// return self;
//}
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
spriing = [Spriing new];
[spriing downloadData:#"http://www.spriing.co.uk/services/"];
SpriingTxt.text = spriing.mStringData;
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
[mRecData release];
[mStringData release];
}
And a separate class;
#import "Spriing.h"
#implementation Spriing
#synthesize mStringData;
#synthesize mRecData;
- (void)downloadData: (NSString*) URL{
mBaseURL = URL;
// Create the request.
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:mBaseURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
mCon=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (mCon)
{
// create var to store data
mRecData = [[NSMutableData data] retain];
}
else
{
// Inform the user that the connection failed.
}
}
//If the connection is reset
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//reset the data length
[mRecData setLength:0];
}
//Obtaining new data
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//Add any newly recieved data to the currently stored data
[mRecData appendData:data];
}
//If something went wrong
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//Release the connection
[mCon release];
//Release the data
[mRecData release];
//Alert the user
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Error!"
message:#"No internet connection!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alert show];
[alert release];
}
//When its done
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//NSLog(#"finished");
// Once this method is invoked, "responseData" contains the complete result
self.mStringData = [[[NSString alloc] initWithData:mRecData encoding:NSUTF8StringEncoding] retain];
//NSLog(#"%#", mStringData);
self.mStringData = [self processData:mStringData];
//NSLog(#"%#", mStringData);
//SpriingTxt.text = mStringData;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//mStringData = nil;
}
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
[mBaseURL autorelease];
mBaseURL = [[request URL] retain];
return request;
}
-(NSString*) processData: (NSString*) string
{
NSMutableString *html = [NSMutableString stringWithCapacity:[string length]];
NSScanner *scanner = [NSScanner scannerWithString:string];
NSString *tempText = nil;
while (![scanner isAtEnd])
{
[scanner scanUpToString:#"<" intoString:&tempText];
if (tempText != nil)
[html appendString:tempText];
[scanner scanUpToString:#">" intoString:NULL];
if (![scanner isAtEnd])
[scanner setScanLocation:[scanner scanLocation] + 1];
tempText = nil;
}
return html;
}
- (void) dealloc
{
[super dealloc];
//[mStringData release];
}
#end
You are starting an asynchronous request for a URL which will take some time. Although it returns immediately, it doesn't imply that the data has been download. NSURLRequest's delegate will be notified when the data has finished downloading. It is not until then that there is data in mStringData which is probably nil prior to being assigned the downloaded data. So when you do SpriingTxt.text = spriing.mStringData; immediately after an asynchronous request without the data being downloaded, SpriingTxt.text is assigned nil.
To resolve this, you can either make a synchronous request which will block until the data has been downloaded which is generally a bad idea or you can message via delegates or notifications to your view controller when the data of your asynchronous request has been downloaded.
To implement the delegate
Delegates are implemented using protocols. You will create a delegate property in the delegating object which would be Spriing as it will let the delegate know when the string has been downloaded and the view controller will be its delegate as it wants to know when the data is available so that it can update its view. Delegates are usually not retained as most times it is the object that creates them that becomes its delegate. So retaining the delegate would create a retain cycle in such instances. There are lots of tutorials about creating the delegates. A rough implementation would be,
in Spriing.h
#protocol SpriinDelegate;
#interface Spriing:... {
id<SpriingDelegate> delegate;
...
}
#property (nonatomic, assign) id<SpriingDelegate> delegate;
...
#end
#protocol SpriingDelegate
- (void)spriing:(Spriing*)aSpriing didFinishDownloadingString:(NSString*)aString;
#end
in Spriing.m
#implementation Spriing
#synthesize delegate;
...
//When its done
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
...
self.mStringData = [self processData:mStringData];
if ( self.delegate && [self.delegate respondsToSelector:#selector(spriing:didFinishDownloadingString:)]) {
[self.delegate spriing:self didFinishDownloadingString:self.mStringData];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
...
#end
in the view controller,
- (void)viewDidLoad {
[super viewDidLoad];
spriing = [Spriing new];
spriing.delegate = self;
[spriing downloadData:#"http://www.spriing.co.uk/services/"];
}
- (void)spriing:(Spriing*)aSpriing didFinishDownloadingString:(NSString*)aString {
SpriingText.text = aString;
}
...

iphone programming + memory leak

I am getting memory leak theFileName = [[responseString lastPathComponent]stringByDeletingPathExtension];
theFileName is a global variable. I have synthesized it and
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// Custom initialization
theFileName = [[NSString alloc] init];
}
return self;
}
- (void)requestFinished:(ASIHTTPRequest *)request{
//internally calls this function
// Use when fetching text data
NSString *responseString = [request responseString];
//NSLog(#"the responsestring for download is:%#",responseString);
theFileName = [[responseString lastPathComponent]stringByDeletingPathExtension];
//NSLog(#"the theFileName for download is:%#",theFileName);
//adds extension .jpg to file name
NSString *jpg=#".jpg";
NSString *addjpg=[theFileName stringByAppendingString:jpg];
//NSLog(#"append %#",addjpg);
}
Released it in dealloc.
-(void)dealloc
{
[thefileName release];
}
}
theFileName = [[responseString lastPathComponent]stringByDeletingPathExtension];
creates a new object for theFileName, which already holds an NSString object. You need to release that old value before, i.e.
[theFileName release];
theFileName = [[responseString lastPathComponent]stringByDeletingPathExtension];
You might consider using a copy (recommended) or retain property for theFilename, and use the dot-syntax in requestFinished:.
Here are a few things that might help.
You're not calling super's dealloc method within self's dealloc. For example,
- (void) dealloc
{
[self.theFileName release];
[super dealloc];
}
You're not using the getters and setters that come with synthesizing a property, and we don't know what property you've used with theFileName. If you've got a retaining property, i.e. a statement like #property (copy) NSString * theFileName then you should use the setter so that you don't trip up on retain counts. For example,
(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
{
// Custom initialization
NSString * aFileName = [[NSString alloc] init];
[self setTheFileName:aFileName];
[aFileName release];
}
return self;
}
is better.

Imlementation of Singleton Class

i have been using mostly the appDelegate class for global variables but recently i came to know that its not a gud way for keeping global variables so i am trying to make a singleton class as following
#implementation globalVar
static globalVar *_sharedInstance =nil;
#synthesize totalTime;
- (id) init
{
if (self = [super init])
{
}
return self;
}
+ (globalVar *) sharedInstance
{
#synchronized (self) {
if (_sharedInstance == nil) {
[[self alloc] init];
}
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (_sharedInstance == nil) {
_sharedInstance = [super allocWithZone:zone];
return _sharedInstance;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
-(NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
// do nothing
}
- (id)autorelease
{
return self;
}
- (void) setTotalTime:(NSString *)time
{
#synchronized(self) {
if (totalTime != time) {
[totalTime release];
totalTime = [NSString stringWithFormat:#"%#",time];
}
}
//NSLog(#"time %#",totalTime);
}
-(NSString *)getTotalTime
{
#synchronized(self) {
//NSLog(#"total %#",totalTime);
return totalTime;
}
}
when i set the value for totaltime in my appDelegate class and retrieve it in that class only i get the correct value. but when i only retrieve the value in some other class i get BAD EXCESS. i first create the sharedinstance and then only call this method then why am i getting this error??
globalVar *myEngine = [globalVar sharedInstance];
NSLog(#"about %#",[myEngine totalTime]);
in my app delegate
globalVar *myEngine = [globalVar sharedInstance];
[myEngine setTotalTime:totalTime];
NSLog(#"in app delegate%#",[myEngine getTotalTime]);
You're releasing totalTime but not retaining the new value, which means that when you access it it's already been released, causing a bad access exception.
You can correct this by changing the line where you set the value to include a call to retain:
totalTime = [[NSString stringWithFormat:#"%#",time] retain];
Have a look at the discussion here:
Is it good practice to use AppDelegate for data manipulation and Handling?

how do you make a "concurrent queue safe" lazy loader (singleton manager) in objective-c

I made this class that turns any object into a singleton, but I know that it's not "concurrent queue safe." Could someone please explain to me how to do this, or better yet, show me the code. To be clear I want to know how to use this with operation queues and dispatch queues (NSOperationQueue and Grand Central Dispatch) on iOS.
Thanks in advance,
Rich
EDIT: I had an idea for how to do it. If someone could confirm it for me I'll do it and post the code. The idea is that proxies make queues all on their own. So if I make a mutable proxy (like Apple does in key-value coding/observing) for any object that it's supposed to return, and always return the same proxy for the same object/identifier pair (using the same kind of lazy loading technique as I used to create the singletons), the proxies would automatically queue up the any messages to the singletons, and make it totally thread safe.
IMHO this seems like a lot of work to do, so I don't want to do it if it's not gonna work, or if it's gonna slow my apps down to a crawl.
Here's my non-thread safe code:
RMSingletonCollector.h
//
// RMSingletonCollector.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "RMWeakObjectRef.h"
struct RMInitializerData {
// The method may take one argument.
// required
SEL designatedInitializer;
// data to pass to the initializer or nil.
id data;
};
typedef struct RMInitializerData RMInitializerData;
RMInitializerData RMInitializerDataMake(SEL initializer, id data);
#interface NSObject (SingletonCollector)
// Returns the selector and data to pass to it (if the selector takes an argument) for use when initializing the singleton.
// If you override this DO NOT call super.
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier;
#end
#interface RMSingletonCollector : NSObject {
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier;
+ (void)destroyCollection;
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
#end
// ==--==--==--==--==Notifications==--==--==--==--==
extern NSString *const willDestroySingletonCollection;
extern NSString *const willDestroySingletonCollectionObject;
RMSingletonCollector.m
//
// RMSingletonCollector.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMSingletonCollector.h"
#import <objc/objc-runtime.h>
NSString *const willDestroySingletonCollection = #"willDestroySingletonCollection";
NSString *const willDestroySingletonCollectionObject = #"willDestroySingletonCollectionObject";
RMInitializerData RMInitializerDataMake(SEL initializer, id data) {
RMInitializerData newData;
newData.designatedInitializer = initializer;
newData.data = data;
return newData;
}
#implementation NSObject (SingletonCollector)
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier {
return RMInitializerDataMake(#selector(init), nil);
}
#end
#interface RMSingletonCollector ()
+ (NSMutableDictionary *)singletonCollection;
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection;
#end
#implementation RMSingletonCollector
static NSMutableDictionary *singletonCollection = nil;
+ (NSMutableDictionary *)singletonCollection {
if (singletonCollection != nil) {
return singletonCollection;
}
NSMutableDictionary *collection = [[NSMutableDictionary alloc] initWithCapacity:1];
[self setSingletonCollection:collection];
[collection release];
return singletonCollection;
}
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection {
if (newSingletonCollection != singletonCollection) {
[singletonCollection release];
singletonCollection = [newSingletonCollection retain];
}
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
id obj;
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
if (obj = [[self singletonCollection] objectForKey:key]) {
return obj;
}
// dynamic creation.
// get a class for
Class classForName = NSClassFromString(className);
if (classForName) {
obj = objc_msgSend(classForName, #selector(alloc));
// if the initializer takes an argument...
RMInitializerData initializerData = [classForName designatedInitializerForIdentifier:identifier];
if (initializerData.data) {
// pass it.
obj = objc_msgSend(obj, initializerData.designatedInitializer, initializerData.data);
}
else {
obj = objc_msgSend(obj, initializerData.designatedInitializer);
}
[singletonCollection setObject:obj forKey:key];
[obj release];
}
else {
// raise an exception if there is no class for the specified name.
NSException *exception = [NSException exceptionWithName:#"com.RMDev.RMSingletonCollector.failed_to_find_class" reason:[NSString stringWithFormat:#"SingletonCollector couldn't find class for name: %#", [className description]] userInfo:nil];
[exception raise];
[exception release];
}
return obj;
}
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier {
id obj = [self collectionObjectForType:className identifier:identifier];
RMWeakObjectRef *objectRef = [[RMWeakObjectRef alloc] initWithObject:obj identifier:identifier];
return [objectRef autorelease];
}
+ (void)destroyCollection {
NSDictionary *userInfo = [singletonCollection copy];
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollection object:self userInfo:userInfo];
[userInfo release];
// release the collection and set it to nil.
[self setSingletonCollection:nil];
}
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollectionObject object:[singletonCollection objectForKey:key] userInfo:nil];
[singletonCollection removeObjectForKey:key];
}
#end
RMWeakObjectRef.h
//
// RMWeakObjectRef.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
// In order to offset the performance loss from always having to search the dictionary, I made a retainable, weak object reference class.
#import <Foundation/Foundation.h>
#protocol RMWeakObjectReference <NSObject>
#property (nonatomic, assign, readonly) id objectRef;
#property (nonatomic, retain, readonly) NSString *className;
#property (nonatomic, retain, readonly) NSString *objectIdentifier;
#end
#interface RMWeakObjectRef : NSObject <RMWeakObjectReference>
{
id objectRef;
NSString *className;
NSString *objectIdentifier;
}
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier;
- (void)objectWillBeDestroyed:(NSNotification *)notification;
#end
RMWeakObjectRef.m
//
// RMWeakObjectRef.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMWeakObjectRef.h"
#import "RMSingletonCollector.h"
#implementation RMWeakObjectRef
#dynamic objectRef;
#synthesize className, objectIdentifier;
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier {
if (self = [super init]) {
NSString *classNameForObject = NSStringFromClass([object class]);
className = classNameForObject;
objectIdentifier = identifier;
objectRef = object;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollectionObject object:object];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollection object:[RMSingletonCollector class]];
}
return self;
}
- (id)objectRef {
if (objectRef) {
return objectRef;
}
objectRef = [RMSingletonCollector collectionObjectForType:className identifier:objectIdentifier];
return objectRef;
}
- (void)objectWillBeDestroyed:(NSNotification *)notification {
objectRef = nil;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[className release];
[super dealloc];
}
#end
The easiest (and thread safe) acces to singleton:
static SomeClass* shared_instance;
+(SomeClass*) sharedInstance {
#synchronized( shared_instance ) {
if( !shared_instance ) {
shared_instance = [[SomeClass alloc] init];
//some additional initialization here
}
}
return shared_instance;
}
Edit:
I think particular singleton class would be your best choice. Even for lazy loading. E.g. you need to access some array of users all over your app. Then you can create singleton class:
UserManager:
+(UserManager*) sharedManager;
-(NSArray*) allUsers;
-(NSArray*) recentUsers;
-(NSArray*) featuredUsers;
-(void) addUser:(User*) user;
-(void) removeUser:(User*) user;
etc...
Then you will be able to access that arrays in every view controller.
You should create singleton for other types.

Updating managed objects in a background thread and displaying them in main thread, what's wrong

Good evening,
I have some problem with CoreData and Concurrency, so I tried the simplest code and it still does not work. Could you indicate me where I am wrong.
I created a "DataManager" which update one CoreData object
#implementation OBSDataManager
#synthesize persistentStoreCoordinator;
- (OBSDataManager *)initWithPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)aPersistentStoreCoordinator {
if (self = [super init]) {
self.persistentStoreCoordinator = aPersistentStoreCoordinator;
}
return self;
}
- (void)dealloc {
[persistentStoreCoordinator release];
[super dealloc];
}
- (void)start {
[self performSelectorInBackground:#selector(updateData) withObject:nil];
}
- (void)updateData {
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = self.persistentStoreCoordinator;
// get chunk if it exists, or create it
OBSChunk *chunk = [OBSChunk theChunkInContext:context];
if (!chunk) {
chunk = [NSEntityDescription insertNewObjectForEntityForName:#"Chunk"
inManagedObjectContext:context];
}
while (1) {
// update content
chunk.text = [[NSDate date] description];
// save it
NSError *error;
if ([context save:&error]) {
NSLog(#"Problem on save");
}
}
[context release];
}
#end
I have a view controller which displays content of my CoreData object
#implementation MainViewController
#synthesize managedObjectContext;
#synthesize label;
#pragma mark -
#pragma mark UIViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(onCoreDataUpdate:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
}
- (void)viewDidUnload {
[super viewDidUnload];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:nil];
}
#pragma mark -
#pragma mark private
- (void)onCoreDataUpdate:(NSNotification *)updateNotification {
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:updateNotification];
OBSChunk *chunk = [OBSChunk theChunkInContext:self.managedObjectContext];
self.label.text = chunk.text;
}
#end
It appears that the chunk object that is fetch in onCoreDataUpdate method has faulted data.
Where am I wrong ?
Regards,
Quentin
-onCoreDataUpdate: is being called on the background thread. Notifications are received on the thread they're sent from. You need to call back to the main thread to actually handle the update. You can use something like the following to handle this:
- (void)onCoreDataUpdate:(NSNotification *)updateNotification {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:_cmd withObject:updateNotification waitUntilDone:NO];
return;
}
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:updateNotification];
OBSChunk *chunk = [OBSChunk theChunkInContext:self.managedObjectContext];
self.label.text = chunk.text;
}