I want to create a new UILocalNotification every time I enter a certain method. I would assume this would have to be done by reading from an array or something along this line but I cannot figure it out. How do I do such a thing dynamically without hardcoding something like the following:
-(void) createNotification
{
UILocalNotification *notification1;
}
Now I would like to be able to create notification2, notification3, etc etc each time I enter createNotification. For the specific reason that then I can cancel the appropriate notification without cancelling them all.
The following is what I have attempted, perhaps Im way off... maybe not. Either way if someone could provide some input, would be appreciated. Thanks!
-(void) AddNewNotification
{
UILocalNotification *newNotification = [[UILocalNotification alloc] init];
//[notificationArray addObject:newNotification withKey:#"notificationN"];
notificationArray= [[NSMutableArray alloc] init];
[notificationArray addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:newNotification,#"theNotification",nil]];
}
You are almost there: using an array is certainly the right thing to do! The only problem is that you keep creating a new instance of the array every time you go through your AddNewNotification method. You should make notificationArray an instance variable, and move its initialization code notificationArray= [[NSMutableArray alloc] init]; to the designated initializer of the class where notificationArray is declared.
If you would like to give each notification that you insert an individual key by which you can find it later, use NSMutableDictionary instead of NSMutableArray. Re-write the AddNewNotification method as follows:
-(void) addNewNotificationWithKey:(NSString*)key {
UILocalNotification *newNotification = [[UILocalNotification alloc] init];
[notificationDict setObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:newNotification,#"theNotification",nil]
forKey:key];
}
When you call the addNewNotificationWithKey: method, you'd be able to provide a key for the newly added notification, for example
[self addNewNotificationWithKey:#"notification1"];
[self addNewNotificationWithKey:#"notification2"];
and so on.
Related
I have a Cocoa app that uses automatic reference counting and does not use core-data (not document-based) and I want to be able to create multiple instances of a window I have defined in a nib file.
I currently have this in my AppDelegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
// for slight performance bump, we assume that users usually have 1 session open.
sessionWindowControllers = [[NSMutableArray alloc] initWithCapacity:1];
}
- (void) awakeFromNib {
// on start, we create a new window
[self newSessionWindow:nil];
}
- (IBAction)newSessionWindow:(id)sender {
SessionWindowController *session = [[SessionWindowController alloc] initWithWindowNibName:#"SessionWindow"];
//add it to the array of controllers
[sessionWindowControllers addObject:session];
[session showWindow:self];
}
SessionWindowController is a subclass of NSWindowController.
but when I run it, I get the runtime error
LayoutManagement[30415] : kCGErrorIllegalArgument:
_CGSFindSharedWindow: WID 11845 Jun 8 18:18:05 system-process LayoutManagement[30415] : kCGErrorFailure: Set a breakpoint #
CGErrorBreakpoint() to catch errors as they are logged. Jun 8
18:18:05 system-process LayoutManagement[30415] :
kCGErrorIllegalArgument: CGSOrderFrontConditionally: Invalid window
Is using NSMutableArray even a good way to manage multiple windows, or is there a better design pattern? Thanks!
Ans. kindly provided by Ben Flynn:
I put the
sessionWindowControllers = [[NSMutableArray alloc] initWithCapacity:1];
in applicationDidFinishLaunching, but since awakeFromNib is called first, we are trying to add an instance of session to the array before it even exists.
Solution: put array init inside the awakeFromNib before we make our first window.
- (void) awakeFromNib {
sessionWindowControllers = [[NSMutableArray alloc] initWithCapacity:1];
[self newSessionWindow:nil];
}
REVISED...
The crux of the app is communicating with a database server. Responses from the server to the app are all in XML. There are several screens. Example, screen 1 lists the user's information, screen 2 lists the user's past trades, allows new trades, and so on.
Here is some code from my AppDelegate:
StartViewController *svc = [[StartViewController alloc] init];
TradeViewController *tvc = [[TradeViewController alloc] init];
CashViewController *cvc = [[CashViewController alloc] init];
ComViewController *covc = [[ComViewController alloc] init];
PrefsViewController *pvc = [[PrefsViewController alloc] init];
NSMutableArray *tabBarViewControllers = [[NSMutableArray alloc] initWithCapacity:5];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:svc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:tvc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:cvc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:covc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:pvc];
[tabBarViewControllers addObject:navigationController];
navigationController = nil;
[tabBarController setViewControllers:tabBarViewControllers];
[[self window] setRootViewController:tabBarController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
Trying to stick with the MVC style, I have a singleton class which does all of the "processing".
Now an example on how I run into a wall… the user can change their email address on screen 5. Enter new email address into text field and click the save button. The button then calls a method from the singleton class which sends the new email address to the server and (via the URL) and receives a XML response confirming the change.
Here are my problems:
1. I start the spinner from the view controller before I make the singleton class method call - but not knowing when the app to server send/receive is finished, how do I make the spinner stop at the right time? I can't of it from the singleton class, I tried that. From what I know, it has to be from within the VC or is there a way to change VC output from my singleton class?
The singleton class NSURLConnection is handling ALL of my communication. Everything from a simple, email change all the way to updating transaction tables. This just seems wrong to me and makes it very difficult to keep track on who is calling what. Again, I am going by my interpretation of MVC. I think it would be much easier to have a NSURLConnection for every VC and do some processing in those classes. However that would not be MVC(ish).
I have close to a 100 variables, arrays, etc… in my singleton class which I use to assign values to all my VC. This also seems wrong to me but I can't think of any other way.
how can I distinguish in the NSURLConnection delegate
(connectionDidFinishLoading) which URL call is being made?
Each of the delegate methods (such as -connectionDidFinishLoading:) has a connection parameter that tells you which connection sent the message. A given connection can only load one URL at a time, so there's a one to one correspondence between URLs and connections.
How can I tell outside of "connectionDidFinishLoading" when the download is completed?
That method tells you when the connection is finished. It's up to you to store that information somewhere where it's useful to your app.
Update: Based on what you've added, your "processing" class is your app's model. The rest of the app shouldn't care that each transaction involves a message to the server -- that's the model's business alone. Also, there's no reason that the model has to be a single object (let alone a singleton) -- it can be a group of objects that work together.
So, you might have a class (let's call it Processor) that represents the application's interface to the model (some might even call this a "model controller"). An instance of Processor might create a local database for storing the current local state of the app.You might also have a Transaction class that represents a single transaction with the server. A transaction could create a request, send it to the server, get the response, update the database, and tell the Processor that the transaction is done. Or, maybe when some other part of the app (like one of your view controllers) asks the Processor to process a new transaction, the Processor passes the requesting object along to the transaction that it creates so that the transaction can update the requestor directly.
It's hard to say what the best plan for your app is without knowing where you're planning on taking it, but the usual guidelines hold:
break your problem into parts that are easier to solve
limit the scope of each class's responsibilities
if something seems to complicated, it probably is
Breaking your model up into several classes will make it easier to test, as well. You can imagine how easy it would be to write a set of unit tests for the Transaction class. The same goes for Processor -- if the server transaction stuff is in a different class, it's easier to test that the Processor is doing the right thing.
If you have multiple NSURLConnections for the same delegate, consider using a global (well, let's say rather an instance variable) NSMutableDictionary instance, in which you store the data depending on which NSURLConnection is being called. You can use, for example, the in-memory address of the connections converted to an NSString (something like
[NSString stringWithFormat:#"%p", connection]
should do the trick).
Also, in the connectionDidFinishLoading: and connection:didFailLoadWithError: methods, remove the keys corresponding to the NSURLConnections. Thus, you can tell it from 'outside' if a connection is finished: just check if it is in the dictionary or not.
If you're downloading any data over a network connection, I would suggest using ASIHttpRequest. This will allow you to download files asynchronously, meaning your interface doesn't freeze during the download process.
If you use ASIHttpRequest, you can also set the didFinishSelector. By doing this, you can control which method is called when a specific URL has finished loading.
Have a look at this:
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
[request setDidFinishSelector:#selector(requestDone:)];
Then:
- (void)requestDone:(ASIHTTPRequest *)request
{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
// If you want, you can get the url of the request like this
NSURL *url = [request url];
}
As for the second part of your question, if the requestDone: method has not been called, you know the download has not completed.
If you want to do something more complicated with multiple downloads, ASIHttpRequest offers queue functionality too. Take a look here.
Hope this will help you.
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString *urlString = [[[connection originalRequest] URL] absoluteString];
if ([urlString caseInsensitiveCompare:#"http://www.apple.com"] == NSOrderedSame) {
//Do Task#1
}
else if ([urlString caseInsensitiveCompare:#"http://www.google.com"] == NSOrderedSame)
{
//Do Task#2
}
}
I would recommend subclassing NSURLConnection. Simply add two properties: an NSInteger, tag, and a BOOL, isFinished. This way, you can #define tags for each different request and then identify them by tag in your delegate methods. In connectionDidFinishLoading, you can set the isFinished BOOL to YES, and then you can check in other methods if then connection is finished.
Here's my own NSURLConnection subclass, TTURLConnection:
TTURLConnection.h:
#import <Foundation/Foundation.h>
#interface TTURLConnection : NSURLConnection <NSURLConnectionDelegate>
#property (nonatomic) NSInteger tag;
#property (nonatomic) BOOL isLocked;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:
(BOOL)startImmediately tag:(NSInteger)tagParam;
#end
TTURLConnection.m:
#import "TTURLConnection.h"
#implementation TTURLConnection
#synthesize tag;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:
(BOOL)startImmediately tag:(NSInteger)tagParam {
self = [super initWithRequest:request delegate:delegate
startImmediately:startImmediately];
if(self) {
self.tag = tagParam;
}
return self;
}
#end
I'm working on a RSS Reader using this tutorial. All table cells data come from a NSMutableArray instance (_allEntries). Then I import
EGOTableViewPullRefresh and add [self refresh] in -(void)reloadTableViewDataSource (self.refresh is a method to populate data of allEntries).
Then pull to refresh works but cells got duplicated every time I refresh. I tried to solve it in two ways.
When download data from internet, add if (![_allEntries containsObject:entry]) before [_allEntries insertObject:entry atIndex:insertIdx] but it didn't work, maybe I should use entry.title or some other attribute in the object to compare but it's not effective.
Like what I did in -viewDidLoad, add self.allEntries = [NSMutableArray array], but I don't know where should I put this line.
Is there anyone who can give me a direction?
[EDIT]
There's no too much logic in viewDidLoad, just
self.allEntries = [NSMutableArray array];
self.queue = [[NSOperationQueue alloc] init]; //add download&parse operation to a queue
self.feeds = [self getFeeds]; //load feeds from local file
And I put [self refresh] in reloadTableViewDataSource, the first time I open my app, there's nothing showed in the tableview. Then I pull to refresh, it works. Then pull to refresh again, it got duplicated.This is the "refresh" method.
- (void)refresh {
for (NSString *feed in _feeds) {
NSURL *url = [NSURL URLWithString:feed];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[_queue addOperation:request];
}
}
I want to rebuild the array so I write self.allEntries = [NSMutableArray array] again but it turns out "Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (140)". So as mentioned, I really get confused about where should I put this line.Thx~~
The logic you have in viewDidLoad that builds your array should be moved to its own method (reloadTableViewData), and then you would just call that method in viewDidLoad.
[self reloadTableViewData];
You would also call that same method when you do the pull to refresh.
Make sure you are rebuilding that array and not just adding objects to the existing one.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I use NSTimer
Decrement Issue
I'm really struggling to get rid of the following warning:
Incorrect decrement of the reference count of an object that is at this point not owned by the caller.
The code compiles fine and the app seems to work fine. Basically I am trying to create an object of a class to play a short audio clip when the button is pressed. I've created a class to play the file and the objects are passed the files name as a string.
Here is the code:
- (IBAction) playKick
{
PlayAudio *thisPlayAudio= [[[PlayAudio alloc] init] playFile:(#"RockSnare")];
[thisPlayAudio release];
}
I've read the other posts and any help would be much appreciated!
I'd have to see the class definition for PlayAudio, but it's doubtful the playFile method returns its PlayAudio instance. You probably want this:
PlayAudio *thisPlayAudio = [[PlayAudio alloc] init];
[thisPlayAudio playFile:(#"RockSnare")];
[thisPlayAudio release];
Maybe this would help:
- (IBAction) playKick {
PlayAudio *thisPlayAudio= [[PlayAudio alloc] init];
[thisPlayAudio playFile:(#"RockSnare")];
[thisPlayAudio release];
}
This finishes creating the object and assigns it to thisPlayAudio, then plays the audio. What you have sets thisPlayAudio to the result of the playFile call.
What is the return type of the playFile: method? Are you sure it returns the same object you call it on?
Maybe your code should be:
PlayAudio *thisPlayAudio= [[PlayAudio alloc] init];
[thisPlaysAudio playFile:#"RockSnare"];
[thisPlaysAudio release];
or even
[[[[PlayAudio alloc] init] autorelease] playFile:(#"RockSnare")];
You are trying to immediately release object after creating. Probably, you also doing something wrong in playFile method. And your player won't play any file since you create and delete it in one scope. Try this:
PlayAudio *thisPlayAudio = nil;
- (IBAction) playKick {
if (thisPlayAudio){
[thisPlayAudio release]; thisPlayAudio = nil;
}
thisPlayAudio= [[PlayAudio alloc] init] autorelease];
[thisPlayAudio playFile:(#"RockSnare")];
}
So I've got an NSFetchedResultsController that I activate on ViewDidLoad with the managedobjectcontext that has been passed on from the appdelegate on load.
I put a predicate on some field let's call it "sectionNumber" and say it needs to equal 1 in my predicate.
NSFetchResultsController works fine until I add a new object to the MOContext...
I use MyMO *newObj = [NSEntityDescription insertnewentity]...
start filling the different fields
[newobj setName:#"me"];
[newobj setAge:12];
etc...
Once I put [newobj setSectionNumber:1] - it finds it at that very instant and causes the app to crash with different weird errors that all lead to EXC_BAD_ACCESS.
All of this happens on the MAIN THREAD.
Any ideas why? How could one get around that?
UPDATE:
It only happens when I use my saveMOC method which is called at the end of an NSXMLParser specific thread I spawned off. The saveMOC is called on a successful parse with the [self performSelectorOnMainThread].... If i just added the extra managedobject via ViewDidLoad (just to check weather this is related somehow to to threading) the problem does NOT occur.
So it's obviously something with the new thread even tho the selector should have been run on the main thread.
UPDATE #2:
This is my spawned thread for the XML Parser:
-(void)getAndParseXML {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DLog(#"Online storage");
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:theUrl];
XMLTranslator *translator = [[XMLTranslator alloc] init];
[parser setDelegate:translator];
if ([parser parse]) {
//success call MOC change routine on main thread
DLog(#"success parsing");
[self performSelectorOnMainThread:#selector(saveMOC:) withObject:translator waitUntilDone:NO];
} else {
DLog(#"error: %#",[parser parserError]);
}
[parser setDelegate:nil];
[parser release];
DLog(#"XML parsing completed");
[pool release];
}
Then this is my saveMOC:
-(void)saveMOC:(XMLTranslator*)translator {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
for (NSDictionary *dict in [translator retrievedData]) {
APost *newPost = [NSEntityDescription insertNewObjectForEntityForName:#"APost"
inManagedObjectContext:managedObjectContext];
//parse time into NSDate
[newPost setTime:[formatter dateFromString:[dict objectForKey:#"time"]]];
//author, category, description
[newPost setAuthor:[dict objectForKey:#"author"]];
[newPost setCategory:[dict objectForKey:#"category"]];
[newPost setContent:[dict objectForKey:#"description"]];
//create a post id so that the validation will be alright
[newPost setPostid:[NSNumber numberWithInteger:[[dict objectForKey:#"postid"] integerValue]]];
[newPost setSectionDesignator:sectionDesignator];
}
This saveMoc method continues and has a [managedobjectcontext save:&error] and more... but it's not relevan to our case as my method crashes I've discovered thru commenting one line after another at the point where I set the sectionDesignator since it equals to the current predicate in my NSFetchedResultsController.
The problem is most likely in the NSFetchedResultsController delegate methods or the lack thereof.
When you add a new object to any context and then save the context, that changes the persistent store which triggers the FRC on any thread to begin an update of the tableview. All the index paths change, especially if you set a value for an attribute used as a sectionNameKeyPath. If the table ask for a cell during the update, it will cause a crash because the table can ask for a cell at a index path rendered invalid by the insertion of the new managed object.
You need to make sure you implement the FRC's delegate methods and that you send the table a beginUpdate message to freeze it while the FRC changes all its index paths.
I am sorry to admit that the problem this whole time was releasing an array that held the sort descriptors in the fetch request that was used within the FRC.
Looking at alot of examples I released that array tho unlike the examples I created my array with [NSArray arrayWithObject:.............];
So there was an overrelease each time the fetch request was accessed more than once.
Feel free to close this. Thank you everybody for your help. I discovered this when peter wrote to look at the whole stack and not just one frame.
I have further analyzed the problem and have realized it occurs inside the loop.
I have further understood that it only happens when I have more than one object, meaning that one FRC takes over after an object insertion into MOC and tries to come back to the for loop, it tries to access an object or a reference that's not there. I haven't found what object causes it and how to retain it properly.
Any suggestions?
Consider the following:
for (int i=0; i<2; i++) {
NSLog(#"%i",i);
APost *thePost = [NSEntityDescription insertNewObjectForEntityForName:#"HWBPost" inManagedObjectContext:managedObjectContext];
[thePost setCategory:#"CAAA"];
[thePost setContent:#"SSSSSS"];
[thePost setSectionDesignator:sectionDesignator];
}
If I change the for loop to i<1 meaning it only runs once, the app does NOT crash. As soon as it is more than one object insertion the app crashes.