I have the Apress "Learn Cocoa" book (published in 2010 BTW) and I am getting a deprecation error on one of the lines. The code is:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.villain = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"Lex Luthor", kName, #"Smallville", kLastKnownLocation, [NSDate date], kLastSeenDate, #"Superman", kSwornEnemy, #"Revenge", kPrimaryMotivation, [NSArray arrayWithObjects:#"Intellect", #"Leadership", nil], kPowers, #"Superhero Action", kPowerSource, [NSNumber numberWithInt:9], kEvilness, [NSImage imageNamed:#"NSUser"], kMugshot, #"", kNotes, nil];
self.villains = [NSMutableArray arrayWithObject:self.villain];
[villainsTableView reloadData];
[villainsTableView selectRow:0 byExtendingSelection:NO];
[self updateDetailViews];
}
I am getting the error on the 2nd to last line and I don't know exactly what that line is intending to do.
The replacement for the deprecated method would look like this:
[villainsTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
The documentation tells you what that message does and what the replacement is.
In XCode you can place the mousecursor above the symbol you want to know something about. While placing the mousecursor above this symbol (i.e. NSString), hold down the ALT-Key and doubleclick. This will open up a context sensitive documentation. All deprecated methods and symbols are marked red there. Mostly the new replacement is documented next to the old one. In the new xcode, a popup-window will appear. The doc will be opened by clicking the "notebook"-icon at the border of the box.
i.e. for NSString you will find:
– initWithCString: Deprecated in iOS 2.0
+ stringWithCString:encoding:
With an educated guess you will choose "+ stringWithCString:encoding:"
Apple almost adds new functionality which is a logical augmentation of the old stuff, so you don't need to google hard, but watch into the method summary of the related doc.
Related
Currently I didn't use ARC in my app, and I tried to create a NSAlert object as a local variable, at the end of function, I didn't release it.
I expected that the app crash with that, the function code is here.
#define GLOBAL_VARIABLE 0
NSString *msgText = [self.defaultValueTextFiled stringValue];
NSString *informativeText = #"Informative Text";
#if !GLOBAL_VARIABLE
NSAlert *alertView = nil;
#endif
if (alertView == nil)
{
alertView = [[NSAlert alloc] init];
[alertView setMessageText:msgText];
[alertView setInformativeText:informativeText];
NSTextField *accessory = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,200,22)];
[accessory setStringValue:#"accessory result"];
[alertView setAccessoryView:accessory];
}
NSView *accessory= nil;
NSInteger result = [alertView runModal];
if (result == NSAlertAlternateReturn)
{
accessory = [alertView accessoryView];
if (accessory != nil)
{
NSTextField *txtFiled = (NSTextField *)accessory;
NSLog(#"%ld", [accessory retainCount]);
NSString *str = [txtFiled stringValue];
NSLog(#"%ld", [accessory retainCount]);
[self.resultValueTextField setStringValue:str];
NSLog(#"%ld", [accessory retainCount]);
}
}
Questions:
(1) There is no [alertView release] in the end, why not crash? It has even no leaks.
(2) Refer to here , the accessory view shouldn't be release. However, I tried to release the view before [alertView runModal], then get its stringValue later, that could works. Why?
(3) The return value of function retainCount is interesting. When I created the alertView object, the retainedCount of alertView is 3. (Why?)
Before [alertView setAccessoryView:accessory], the accessory's retainedCount is 1, then it changed to 2 after executing it. That's normal and right. However, the log result for the above code, they're 20, 21, 22. How did they come from?
Thanks for your attention!
It does leak. Put a breakpoint on dealloc (or create a subclass of NSAlert and add a log statement to dealloc) to show this
The accessry view should be released. You alloc it and then it would be retained by the alertView
Here's a concise guide on when to use retainCount.
The rules for memory management are generally quite simple. Most problems come from overthinking them and trying to guess what other parts of the system are going to do, rather than just following the rules as written.
The first rule of Memory Management:
Use ARC.
If you cannot follow rule one (for instance, you are developing for OS X 10.5 as I do), then here are the rules:
You must balance each call you make to +alloc…, +new…, -…copy… or -retain with a call to -release or -autorelease.
That's really it. Everything else is really just commentary to help you follow that rule. So, you called [NSAlert alloc] and [NSTextField alloc], you need to call release on those objects.
Why doesn't it crash?
Because a leak is not going to crash unless it causes you to run out of memory.
Why doesn't it leak?
The most likely cause is that you're only running it once and then expecting Instruments to detect the leak. It may not show up if you only run it once. It depends on how NSAlert is internally implemented. Instruments finds leaks by walking all the pointers in the system and determining if any accessible pointers still reference a piece of allocated memory. There are many cases when a "leak is not a leak."
But this also suggests you're not running the static analyzer, because the analyzer should definitely have detected that leak. Run the static analyzer (Cmd-Shift-B) all the time and clean up what it finds.
the accessory view shouldn't be release.
That's not what the link you reference says. You shouldn't add an extra release. But in the referenced code, you'll notice that they release the view to balance their own +alloc.
… retainCount …
Never use -retainCount. Not even for debugging. Not even for anything else. There is no point at which retainCount is going to return you a piece of information that is going to be more enlightening than confusing. Why is it greater than you think it should be? Because other objects are retaining the alert view? Which objects? That's not your business. That's an internal implementation detail, subject to change. It could be pending autorelease calls, which show up temporarily as a "too high retainCount". It could be the run loop. It could be an internal controller. It could be anything. Calling retainCount tells you nothing useful.
Now that you understand manual memory management, switch to ARC and think about object graphs rather than retain counts. It is a much better way to deal with memory.
I have searched SO among other fora for the answer to this, but whatever I try, it does not seem to get rid of my memory leak. Does anybody have any suggestions?
I get a memory leak of 16 bytes per NSDate in my ViewController every time I present it.
-(void)initialise:(id)sender withDate:(NSDate *)date withMinimumDate:(NSDate *)minimum {
delegate = sender;
self.originalDate = [[NSDate alloc] init];
self.originalDate = date;
self.minimumDate = [[NSDate alloc] init];
self.minimumDate = minimum;
}
- (void)dealloc {
[self.originalDate release];
[self.minimumDate release];
[super dealloc];
}
I have tried releasing and/or making the NSDates nil before allocating them for the first time, and setting them to nil in the dealloc method.
They are declared as nonatomic and retained properties in the Header.
Have I missed something basic here or is there a more complex answer?
You are doing an alloc/init of a NSDate for originalDate, but then immediately abandoning it and setting it equal to the date parameter, possibly leaking the NSDate you created. You're doing this with minimumDate, too.
At a minimum, remove those two lines that do the alloc/init of the two respective NSDate objects that you subsequently abandon in favor for the parameters to the initialise method.
By the way, this problem (and the dealloc problem) would have been highlighted for you if you employed the static analyzer ("Analyze" on "Product" menu or press shift+command+B):
When writing manual reference counting code, the static analyzer is invaluable for identifying issues. You really should have a clean bill of health from the analyzer. You can click on the blue icons and it will often expand with the descriptions and arrows that I show in the above screen snapshot.
Note, this also highlighted the inappropriate use of self.originalDate (instead of _originalDate, or whatever the backing instance variable is) in your dealloc method. As I mentioned in my earlier comment, you should not use accessor methods in dealloc method.
These two lines are creating references to objects that are never released.
self.originalDate = [[NSDate alloc] init];
self.minimumDate = [[NSDate alloc] init];
Seeing as you instantly overwrite these properties with new values, you just need to delete these two lines (which are redundantly allocating NSDate objects that are never used) and your leak should go away.
You should also check whether delegate is properly memory managed too, as I see no release in dealloc for the delegate variable.
I've seen in this question that CKRefreshControl can be used as a substitute for UIRefreshControl for apps that support both iOS5 and iOS6. I've found the code on Github, but I don't know how to implement it. John said to just use the same code. But something is missing. Where does the CKRefreshControl code go?
self.refreshControl = [[UIRefreshControl alloc] init];
Thanks!
There is no specific CKRefreshControl code needed other than the CKRefreshControl source itself. When CKRefreshControl was first released you had to replace all calls to UIRefreshControl with calls to CKRefreshControl, and then it automatically dispatched to the correct class based on whether you were on iOS 5 or on iOS 6+.
However, with the recent contributions from John Haitas, that is no longer necessary. Instead, simply compiling and linking against the CKRefreshControl source code makes the UIRefreshControl class available when targeting iOS 5. As a result, you can simply continue to use [[UIRefreshControl alloc] init], and it will automatically work on iOS 5.
Why should you believe me? Because I'm the guy who wrote CKRefreshControl in the first place.
1) Add the 3 classes and prefix to your project:
- CKParagraphStyle.h and CKParagraphStyle.m
- CKParagraphStyle.h and CKRefreshArrowView.m
- CKRefreshControl.h and CKRefreshControl.m
- CKRefreshControl-Prefix.pch (goes into TableVCs using Refresh).
2) Add the QuartzCore.framework to the target libraries.
3) Add this method:
-(void)doRefresh:(CKRefreshControl *)sender {
NSLog(#"refreshing");
[self.refreshControl performSelector:#selector(endRefreshing) withObject:nil afterDelay:1.0];
}
Finally, use UIRefreshControl, as usual, but select the doRefresh method:
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(doRefresh:) forControlEvents:UIControlEventValueChanged];
I am currently doing the CS193P lessons via iTunesU and the teacher mentioned the Build and Analyze option several times. He said that it was a nice tool and fun to play with.
So I tried, and noticed that it doesn't work, or that I don't understand how it should work (I think the last option).
I have a few memory leaks, and it is not warning me at all! I saw online that a blue thing should appear telling me it is a leak, but I don't see anything although I'm doing NSDictionary *dict = [[NSDictionary alloc] init];.
How is it supposed to work? From what I read on the internet I thought it should signal potential leaks. What am I doing wrong?
I'm using XCode 3.2.5.
Thanks.
Update:
This is a kind of bug, I think.
When I declare this in the interface like NSDictionary *dict; and initialize it (but nowhere deallocating it) it says nothing.
When I declare and initialize it in - (void) init and don't release it in there like:
- (void) init {
if(self = [super init])
NSDictionary *dict = [[NSDictionary alloc] init];
return self;
}
It does signal a leak. Why? Is this because of my settings? Is this a bug? If it is a bug, where and how should I report it?
It's giving you a warning because you're not deallocating it.
-(void)dealloc{
[super dealloc];
[dict dealloc];
}
It's not warning you because you should be able to release the objects as soon as you create them, and the analyzer goal is to alert you on possible leaks in your code.
You can either use autorelease, or you dealloc the object you create manually.
P.S., little curiosity: why are you using Xcode 3.2.5?
Don't know exactly if that version can, but in the latest versions of Xcode, when you run that tool, you are able to see WHAT object you are deallocating with the means of some arrows with explanation, something like
I just found out that a reboot and restart of Xcode will bring it back.
In most systems, the default behaviour for "open a new window" is that it appears at the front. This doesn't happen in Cocoa, and I'm trying to find the "correct" way to make this standard behaviour. Most things I've tried only work for a maximum of one window.
I need to open multiple windows on startup:
(N x NSDocuments (one window each)
1 x simple NSWindowController that opens a NIB file.
Things that DON'T work:
Iterate across all the NSDocuments I want to open, and open them.
What happens? ... only the "last" one that call open on comes to the front - the rest are hidden, invisible, nowhere on the screen, until you fast-switch or use the Window menu to find them.
Code:
...documents is an array of NSPersistentDocument's, loaded from CoreData...
[NSDocumentController sharedDocumentController];
[controller openDocumentWithContentsOfURL:[documents objectAtIndex:0] display:YES error:&error];
Manually invoking "makeKeyAndOrderFront" on each window, after it's opened
What happens? nothing different. But the only way I can find to get the NSWindow instance is so horribly hacky it seems totally wrong (but is mentioend in several blogs and mailing list posts)
Code:
[NSDocumentController sharedDocumentController];
NSDocument* openedDocument = [controller openDocumentWithContentsOfURL:[documents objectAtIndex:0] display:YES error:&error];
[[[[openedDocument windowControllers] objectAtIndex:0] window] makeKeyAndOrderFront:nil];
...I know I'm doing this wrong, but I can't find out why/what to do differently :(.
Something that works, usually, but not always:
As above, but just use "showWindow" instead (I took this from the NSDocument guide).
Bizarrely, this sometimes works ... even though it's the exact code that Apple claims they're calling internally. If they're calling it internally, why does it behave different if I re-invoke it after they've already done so?
[[[openedDocument windowControllers] objectAtIndex:0] showWindow:self];
You can just open all the documents without displaying and then tell the documents to show their windows:
NSArray* docs = [NSArray arrayWithObjects:#"doc1.rtf", #"doc2.rtf",#"doc3.rtf",#"doc4.rtf",nil];
for(NSString* doc in docs)
{
NSURL* url = [NSURL fileURLWithPath:[[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"] stringByAppendingPathComponent:doc]];
NSError* err;
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:NO error:&err];
}
[[[NSDocumentController sharedDocumentController] documents] makeObjectsPerformSelector:#selector(showWindows)];
Won't this work?
For 10.6 or greater
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
This often has something to do with the app itself: your other windows are behind other apps (in particular, behind Xcode!), and would have appeared with a Hide Others command.
The solution to that problem would be that after you send showWindow to all of your windows (making sure you do the key one last), you tell the app to come forward, relative to other apps.
NSApp.activateIgnoringOtherApps(true) // Swift
or
[NSApp activateIgnoringOtherApps:YES]; // Objective-C
See also: How to bring NSWindow to front and to the current Space?