NSCFArray leak in the NSMutablearray allocation - objective-c

I am getting the leak at this allocation
filteredListContent = [[NSMutableArray alloc] initWithCapacity:[showList count]];
CODE:
-(void)reloadTable
{
EventListAppDelegate *appDelegate;
UIApplication app = [UIApplication sharedApplication];
appDelegate = (EventListAppDelegate *)[app delegate];
contactList = [appDelegate getAllContactsList];
inviteeList = [appDelegate getInviteeListForEvent:event.primaryKey];
if (isInvited == YES)
{
showList = [appDelegate getInviteeListForEvent:event.primaryKey];
}
else
{
showList = [appDelegate getAllContactsList];
}
filteredListContent = [[NSMutableArray alloc] initWithCapacity:
[showList count]];
[filteredListContent addObjectsFromArray: showList];
[self organizeContactItemsIntoIndexes];
self.title = [event.name capitalizedString];
[self getToolbar];
[theTableView reloadData];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[filteredListContent removeAllObjects];
ContactDTO *currentElement;
NSRange range;
for (currentElement in showList)
{
range = [currentElement.lastName rangeOfString:searchText
options:NSCaseInsensitiveSearch];
if(range.location == 0)
{
[filteredListContent addObject:currentElement];
}
}
[self organizeContactItemsIntoIndexes];
[theTableView reloadData];
}
- (void)dealloc
{
[filteredListContent release];
[super dealloc];
}

Your code will allocate a new instance of filteredListContent every time reloadTable is called, which will usually happen several times during the lifetime of your application. This causes a leak because the old instances are not released.
The best (and easiest) way to fix it would be to make filteredListContent a retain property:
in your class header:
#property (nonatomic, retain) NSMutableArray * filteredListContent;
in your reloadTable method:
self.filteredListContent = [NSMutableArray arrayWithCapacity:[showList count]];
Note the use of self. in the second code snippet. That syntax informs Cocoa that it should use the property accessor to set the value of filteredListContent, which will then send the appropriate retain and release messages for you.

You've posted three nearly-identical questions pertaining to memory leaks. It might be helpful for you to read through Apple's Memory Management Programming Guide.

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).

ARC Objective-C: How can self be deallocated?

I am creating a workflow to navigate through websites, every step of the workflow has to load n frames and then knows its ready (I have to implement the timeout).
I don't understand why [self next] is giving me this error:
* -[WebWorkflow next]: message sent to deallocated instance 0x105796ef0
Considering this delegate function:
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
frameCounter++;
NSInteger frames = [(WebWorkflowStep *)[steps objectAtIndex:index] frames];
NSLog(#"Frame counter %ld of %ld", frameCounter, frames);
[self next];
}
And this next method:
-(void) next
{
if ( index < [steps count])
{
frameCounter = 0;
index = index + 1;
WebWorkflowStep *step = [steps objectAtIndex:index-1];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:step forKey:#"selector"];
[[NSNotificationCenter defaultCenter] postNotificationName:EVENT_WORKFLOW_NEXT object:nil userInfo:userInfo];
}
}
Notes:
- WebWorflow a.k.a 'self' has been created/binded by another class with strong
Like so:
#interface AController : NSObject <APIProtocol>
{
WebView *webview;
NSMutableArray *accounts;
WebWorkflow *workflow;
}
#property (strong) WebWorkflow *workflow;
...
I do create the workflow like this:
workflow = [[WebWorkflow alloc] initWithWebView:webview];
NSArray *getPicturesWorkflow = [NSArray arrayWithObjects:
[[WebWorkflowStep alloc] initWithSelector:#"open" andLoadFrames:0],
[[WebWorkflowStep alloc] initWithSelector:#"login" andLoadFrames:2],
[[WebWorkflowStep alloc] initWithSelector:#"getPictures" andLoadFrames:8],
nil];
[workflow setSteps:getPicturesWorkflow];
And it gets initialized like:
-(id)initWithWebView:(WebView *)webview
{
self = [ super init];
if(self) {
timeout = 10;
index = 0;
web = webview;
frameCounter = 0;
[web setFrameLoadDelegate:self];
}
return self;
}
The AController instance owns a web view and is the web view's delegate. The AController instance is getting released (for some reason...we'd need to see how it's owner manages it). Since it might get released during a load, it should clean up after itself as follows:
- (void)dealloc {
[web stopLoading:self]; // or webView, not sure what you call it
}
This will prevent the crash. It will also abandon the load. If you don't want to do that, you'll need to figure out why the AController instance is being released.
The first step in doing that would be a breakpoint in the dealloc method.

NSMutableArray to NSString and Passing NSString to Another View IOS5.1

I have an NSMutableArray of names. I want the pass the data (selected name) inside of NSMutableArray as text to another view's label.
FriendsController.m:
- (void)viewDidLoad {
[super viewDidLoad];
arrayOfNames=[[NSMutableArray alloc] init];
arrayOfIDs=[[NSMutableArray alloc] init];
userName=[[NSString alloc] init];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
long long fbid = [[arrayOfIDs objectAtIndex:indexPath.row]longLongValue];
NSString *user=[NSString stringWithFormat:#"%llu/picture",fbid];
[facebook requestWithGraphPath:user andDelegate:self];
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]];
FriendDetail *profileDetailName = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
profileDetailName.nameString=userName;
[profileDetailName release];
}
- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSData class]]) {
transferImage = [[UIImage alloc] initWithData: result];
FriendDetail *profileDetailPicture = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
[profileDetailPicture view];
profileDetailPicture.profileImage.image= transferImage;
profileDetailPicture.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:profileDetailPicture animated:YES];
[profileDetailPicture release];
}
}
In FriendDetail.h
NSString nameString;
IBOutlet UILabel *profileName;
#property (nonatomic, retain) UILabel *profileName;
#property (nonatomic, retain) NSString *nameString;
In FriendDetail.m
- (void)viewDidLoad
{
[super viewDidLoad];
profileName.text=nameString;
}
nameString in second controller(FriendDetail) returns nil. When i set a breakpoint in firstcontroller I see the string inside of nameString is correct but after that it returns to nil somehow.
-----------------------EDIT----------------------------------------
According to answers I have improved my code little bit
FriendsController.h
FriendDetail *friendController;
#property (strong, nonatomic) FriendDetail *friendController;
FriendsController.m
- (void)viewDidLoad
{
[super viewDidLoad];
arrayOfNames=[[NSMutableArray alloc] init];
arrayOfIDs=[[NSMutableArray alloc] init];
arrayOfThumbnails=[[NSMutableArray alloc] init];
userName=[[NSString alloc] init];
friendController= [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
}
-(void)request:(FBRequest *)request didLoad:(id)result{
if ([result isKindOfClass:[NSData class]])
{
transferImage = [[UIImage alloc] initWithData: result];
friendController.nameString=userName;
[friendController view];
friendController.profileImage.image= transferImage;
friendController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:friendController animated:YES];
}
//this is how i take facebook friends list
if ([result isKindOfClass:[NSDictionary class]]){
items = [[(NSDictionary *)result objectForKey:#"data"]retain];
for (int i=0; i<[items count]; i++) {
NSDictionary *friend = [items objectAtIndex:i];
long long fbid = [[friend objectForKey:#"id"]longLongValue];
NSString *name = [friend objectForKey:#"name"];
NSLog(#"id: %lld - Name: %#", fbid, name);
[arrayOfNames addObject:[NSString stringWithFormat:#"%#", name]];
[arrayOfIDs addObject:[NSNumber numberWithLongLong:fbid]];
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
long long fbid = [[arrayOfIDs objectAtIndex:indexPath.row]longLongValue];
NSString *user=[NSString stringWithFormat:#"%llu/picture",fbid];
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]];
[facebook requestWithGraphPath:user andDelegate:self];
[username retain]
}
Now when i select row first time it sends name. When i come back to tableview and select another name it shows the old name.
If I delete [username retain] in didSelectRowAtIndexPath: it still sends nil to nameString
when I set break point at didSelectRowAtIndexPath: at line `
userName=[NSString stringWithFormat:#"%#",[arrayOfNames objectAtIndex:indexPath.row]]`
I can see userName = #"Adam Dart" which is correct
in my second breakpoint at line friendController.nameString=userName; I see that nameString =nil and userName = Variable is not CFString
ARC is set to NO
The value is nil because you did not pass the value in request:didLoad: function.
In function didSelectRowAtIndexPath, You create a local instance of another ViewController and set the value of nameString, but you did not present the view and release the ViewController immediately. You actually do nothing in these few lines of code:
FriendDetail *profileDetailName = [[FriendDetail alloc] initWithNibName: #"FriendDetail" bundle: nil];
profileDetailName.nameString = userName;
[profileDetailName release];
In function request:didLoad:, again you create a local instance of another ViewController with image. But this instance is only local to this function, which means no relation to the one created in didSelectRowAtIndexPath.
What you need to do is, remember the name of clicked row first in didSelectRowAtIndexPath, here you dont have to create the ViewController instance. When the request finish, set both the image and name to the controller and then present it. But you should avoid user from clicking different rows at the same time, because you don't know when the request finish.
You have two instances of FriendDetail called profileDetailPicture. Both of theses profileDetailPicture are not the same. So in your didSelectRowAtIndexPath method, the value that you assigned to the nameString will not be visible/available to the nameString of the profileDetailPicture In the request:(FBRequest *)request didLoad method.
Edit for solution:
Create an iVar or property (profileDetailPicture) in the FriendController.
Only do one allocation in the request:(...) method.
Remove the allocation statement in the didSelectRowAtIndexPath.
Any chance it has to do with the fact that you assign to profileDetailName and then immediately release it?
profileDetailName.nameString=userName;
[profileDetailName release];
You have to allocate the "first_controller" in your "second_controller"
to pass objects such as your string. and you would call the nameString differently.
example:
second_controller.h
#import "first_controller.h"
...
#interface second_controller : UIViewController{
first_controller* firstController;
}
second_controller.m
- (void)viewDidLoad {
[super viewDidLoad];
firstController = [[first_controller alloc] init];
profileName.text = firstController.nameString;
}
Which you'll have to init it correctly, because its two views sharing information.

NSArray causes EXC_BAD_ACCESS

I declare an NSArray in one class like this:
.h
#interface HTTP : NSObject {
NSArray *listOfProfiles;
}
#property (nonatomic, retain) NSArray *listOfProfiles;
.m
-(id) init {
if ((self = [super init])) {
listOfProfiles = [[NSArray alloc] init];
}
return self;
}
-(void) someMethod {
...
case GET_LIST_OF_PROFILES:
listOfProfiles = [result componentsSeparatedByString:#"^-^"];
NSLog(#"first break: %#",[listOfProfiles objectAtIndex:0]);
break;
...
}
I can access it here just fine, then when I try to access it in another class after creating an object I receive the error EXC_BAD_ACCESS and the debugger goes to main.m:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
http = [[HTTP alloc] init];
[http createProfileArray];
profileListDelay = [[NSTimer alloc] init];
profileListDelay = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(profileListSelector) userInfo:nil repeats:YES];
}
- (void) profileListSelector
{
if (http.activityDone)
{
// http.listofprofiles mem leak?
for (int i = 0; i < http.listOfProfiles.count; i++)
{
NSLog(#"%#",[http.listOfProfiles objectAtIndex:i]);
}
[profileListDelay invalidate];
profileListDelay = nil;
}
}
I'm thinking it's a memory issue maybe, but I could be completely wrong.
It is a memory issue
It is in someMethod
-(void) someMethod {
...
case GET_LIST_OF_PROFILES:
listOfProfiles = [result componentsSeparatedByString:#"^-^"];
NSLog(#"first break: %#",[listOfProfiles objectAtIndex:0]);
break;
...
}
componentsSeparatedByString: returns an autoreleased object
Since you declared the array as a retain property, you should update it like so:
self.listOfProfiles = [result componentsSeparatedByString:#"^-^"];

Improve Load Time of Sectioned UITableView

I am displaying a UITableView modally, but it takes about two seconds for it to appear, below is the code that is holding up the transition.
ModalViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// get all songs from iTunes library
MPMediaQuery *songQuery = [MPMediaQuery songsQuery];
// put the songs into an array
self.songsArray = [songQuery items];
// create a sectioned array where songs are sectioned by title
self.sectionedSongsArray = [self partitionObjects:self.songsArray collationStringSelector:#selector(title)];
}
- (NSArray *)partitionObjects:(NSArray *)array collationStringSelector:(SEL)selector
{
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSInteger sectionCount = [[collation sectionTitles] count];
NSMutableArray *unsortedSections = [NSMutableArray arrayWithCapacity:sectionCount];
for(int i = 0; i < sectionCount; i++)
{
[unsortedSections addObject:[NSMutableArray array]];
}
for (id object in array)
{
NSInteger index = [collation sectionForObject:object collationStringSelector:selector];
[[unsortedSections objectAtIndex:index] addObject:object];
}
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (NSMutableArray *section in unsortedSections)
{
[sections addObject:[collation sortedArrayFromArray:section collationStringSelector:selector]];
}
return sections;
}
The above code works fine, but its slow to load the modal view first time, is there a better way to do this? Thanks.
Yeah: don’t do it in -viewDidLoad. A better place would be in the view controller’s -init or -initWithNibNamed:bundle: or whatever, and in the background. Example:
- (id)init
{
self = [super init];
if(self)
{
// ...
dispatch_async(dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAULT, 0), ^{
// since it's not on the main thread, you need to create your own autorelease pool to prevent leaks
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MPMediaQuery *songQuery = [MPMediaQuery songsQuery];
self.songsArray = [songQuery items];
self.sectionedSongsArray = [self partitionObjects:self.songsArray collationStringSelector:#selector(title)];
// UI calls have to be on the main thread, so we go back to that here
dispatch_async(dispatch_get_main_queue(), ^{
if([self isViewLoaded])
{
[self.tableView reloadData];
}
});
// this releases any objects that got autoreleased earlier in the block
[pool release];
});
}
return self;
}
Your -tableView:numberOfRowsInSection: method should of course now check whether sectionedSongsArray is non-nil and in that case return 0 (or 1 if you want to display a “loading” cell, which you probably should).