I have a leak with the following code that builds up! Why do I have this issue? Is it due to it copying the properties inside the NSString. Is there a way around this?
#property (nonatomic, copy) NSString *reg;
#property (nonatomic, copy) NSString *reg2;
#property (nonatomic, copy) NSNumber *altitude;
#property (nonatomic, copy) NSNumber *heading;
-(void)updateTitles{
self.title=[NSString stringWithFormat:#"%# %#",self.reg,self.reg2];
self.subtitle = [NSString stringWithFormat:#"%#ft %#°",self.altitude,self.heading];
}
The leak is 50% on each of the setting of properties inside this method.
UPDATE
Turns out this was being called from a block ultimately. To try and work around this I did the following.
The following works but still leaks, clear now that self is retained.
-(void)updateTitles{
__block NSString *thisTitle = [[NSString alloc] initWithFormat:#"%# %#",self.reg1,self.reg2];
__block NSString *subTitle = [[NSString alloc] initWithFormat:#"%#ft %#°",self.altitude,self.heading];
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue,^(){
self.title=thisTitle;
self.subtitle = subTitle;
[thisTitle release];
[subTitle release];
});
}
However that leaks and the following which should in theory work gives an unrecognised selector on the setTitle method!!!!!
-(void)updateTitles{
__block NSString *thisTitle = [[NSString alloc] initWithFormat:#"%# %#",self.reg1,self.reg2];
__block NSString *subTitle = [[NSString alloc] initWithFormat:#"%#ft %#°",self.altitude,self.heading];
__block __typeof__(self) blockSelf = self;
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue,^(){
[blockSelf setTitle:thisTitle];
[blockSelf setSubtitle:subTitle];
[thisTitle release];
[subTitle release];
});
}
Assuming you're not using ARC, does the object that has the properties above have a dealloc method, and is it releasing the ivars correctly? Is this object itself being released by any objects that are retaining it?
Does overriding the getters instead of setting the title/subtitle make any difference:
-(NSString *) title
{
return [NSString stringWithFormat:#"%# %#",self.reg,self.reg2];
}
etc.
Related
I have the following properties defined:
#property (nonatomic, retain) NSString *name_;
#property (nonatomic, retain) NSString *profilePicture_;
#property (nonatomic, retain) NSString *username_;
#property (nonatomic, retain) NSString *id_;
and I set them up in my init... like this:
-(id)initWithData:(NSDictionary *)data
{
self = [super init];
if (!self) {
return nil;
}
name_ = [data valueForKey:#"full_name"];
profilePicture_ = [data valueForKey:#"profile_picture"];
username_ = [data valueForKey:#"username"];
id_ = [data valueForKey:#"id"];
return self;
}
with the following dealloc:
-(void)dealloc
{
[name_ release];
[username_ release];
[profilePicture_ release];
[id_ release];
[super dealloc];
}
However the dealloc gives me an error:
pointer being freed was not allocated
Why is this? Do I have to do [[NSString alloc] init...] or [NSString stringWithString:]?
valueForKey: will return an autoreleased object, therefore you have no ownership. As they are all strings you can just call copy like this
name_ = [[data valueForKey:#"full_name"] copy];
profilePicture_ = [[data valueForKey:#"profile_picture"] copy];
username_ = [[data valueForKey:#"username"] copy];
id_ = [[data valueForKey:#"id"] copy];
you should also change your #property declarations to use copy as this is generally recommended for strings.
The other alternative is to go through the synthesised accessors but I generally avoid doing this in either init or dealloc
This is because you are assigning to backing variables in your initWithData. You should use rewrite your code as follows:
self.name_ = [data valueForKey:#"full_name"];
self.profilePicture_ = [data valueForKey:#"profile_picture"];
self.username_ = [data valueForKey:#"username"];
self.id_ = [data valueForKey:#"id"];
This would assign values through properties, which calls [retain] for you. The way your code is written now, the pointer is simply copied into ivars without calling [retain], which ultimately causes the issue that you describe.
Since you are using data from dictionary, you should set values through properties using self keyword.
If it doesn't solve, then problem is probably not inside your class but perhaps where you create the instance of it. Try examining the Code where you allocate and release the instance of this Class.
You should also profile your app using Simulator & NSZombies n Determine where you over-release the object.
So I need to add the objects from an NSArray that the user has chosen using an NSOpenPanel and put all the filenames into this array. Then I have an NSMutableArray called arguments that I am putting the arguments programmatically. Then I need to add these objects from the NSArray to the end of this NSMutableArray. So I use [NSMutableArray addObjectsFromArray:NSArray] and that keeps giving me an error.
This is what I'm doing with the code:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface ZipLockAppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSTextField *input;
IBOutlet NSTextField *output;
IBOutlet NSTextField *password;
NSArray *filenames;
NSMutableArray *arguments;
NSArray *argumentsFinal;
}
#property (assign) IBOutlet NSWindow *window;
#property (retain) NSArray *filenames;
#property (copy) NSMutableArray *arguments;
- (IBAction)chooseInput:(id)sender;
- (IBAction)chooseOutput:(id)sender;
- (IBAction)createZip:(id)sender;
#end
AppDelegate.m
#import "ZipLockAppDelegate.h"
#implementation ZipLockAppDelegate
#synthesize window = _window;
#synthesize filenames;
#synthesize arguments;
- (IBAction)chooseInput:(id)sender {
NSOpenPanel *openZip = [[NSOpenPanel alloc] init];
[openZip setCanChooseFiles:YES];
[openZip setCanChooseDirectories:YES];
[openZip setCanCreateDirectories:NO];
[openZip setAllowsMultipleSelection:YES];
[openZip setTitle:#"Select All Files/Folders to be zipped"];
int result = [openZip runModal];
if (result == 1) {
filenames = [openZip filenames];
}
}
- (IBAction)createZip:(id)sender {
[progress startAnimation:self];
arguments = [NSMutableArray arrayWithObjects:#"-P", [password stringValue], [output stringValue], nil];
[self.arguments addObjectsFromArray:filenames];
argumentsFinal = [[NSArray alloc] initWithArray:self.arguments];
NSTask *makeZip = [[NSTask alloc] init];
[makeZip setLaunchPath:#"/usr/bin/zip"];
[makeZip setArguments:argumentsFinal];
[makeZip launch];
[makeZip waitUntilExit];
[progress stopAnimation:self];
}
And this is the error I keep getting in the log. I can't figure out why I'm getting this.
EXC_BAD_ACCESS(code=13,address=0x0)
This points to the line [arguments addObjectsFromArray:filenames];
I can only make out the first part about the selector and the instance but I don't know what it means. Help...
Be consistent. To begin with, prefix all your instance variables with an underscore, not just some of them.
// Change this...
#synthesize window = _window;
#synthesize filenames;
#synthesize arguments;
//...to this
#synthesize window = _window;
#synthesize filenames = _filenames;
#synthesize arguments = _arguments;
Then you won't be able to do this anymore:
arguments = [NSMutableArray arrayWithObjects:#"-P", [password stringValue], [output stringValue], nil];
Note that on the very next line, you're doing this:
[self.arguments addObjectsFromArray:filenames];
Again, being consistent in using properties rather than directly accessing instance variables will help you avoid these kinds of errors. So rewrite the previous line to use the property, like so:
self.arguments = [NSMutableArray arrayWithObjects:#"-P", [password stringValue], [output stringValue], nil];
The compiler translates self.arguments = someArg into [self setArguments:someArg]. In this case the setter method is needed to retain the object so that it won't be deallocated while the reference is still stored in the _arguments instance variable.
Have you tried debugging it to see if filenames is ever initialized? Looking at your crashlog, you are referencing a nil pointer. It seems either filenames or arguments is never initialized.
Also, try and set your arguments array to a retain type rather than copy.
You also seem to keep going back and forth with your references. If you don't want to keep typing self.arguments, create an ivar in your synthesize statement and just stick with that.
#synthesize filenames = _filenames;
#synthesize arguments = _arguments;
[_arguments addObjectsFromArray:_filenames];
Your arguments object is an NSArray, not an NSMutableArray. You can only add objects to a mutable array, by definition. Try this:
arguments = [NSMutableArray arrayWithObjects:#"-P", [password stringValue], [output stringValue], nil];
I am blocking into something and I am sure it is too big.
I have a custom object that look like this
#interface DownloadObject : NSObject <NSCoding>{
NSNumber *key;
NSString *name;
NSNumber *progress;
NSNumber *progressBytes;
NSNumber *size;
NSString *path;
}
#property (copy) NSNumber *key;
#property (copy) NSString *name;
#property (copy) NSNumber *progress;
#property (copy) NSNumber *size;
#property (copy) NSString *path;
#property (copy) NSNumber *progressBytes;
-(id)initWithKey:(NSNumber *)k name:(NSString *)n progress:(NSNumber *)pro size:(NSNumber *)s path:(NSString *)p progressBytes:(NSNumber *)pb;
#end
And the implementation
#implementation DownloadObject
#synthesize size, progress, name, key, path, progressBytes;
-(id)initWithKey:(NSNumber *)k name:(NSString *)n progress:(NSNumber *)pro size:(NSNumber *)s path:(NSString *)p progressBytes:(NSNumber *)pb
{
self.key = k;
self.name = n;
self.progress = pro;
self.size = s;
self.path = p;
self.progressBytes = pb;
return self;
}
-(id) initWithCoder: (NSCoder*) coder {
if (self = [super init]) {
self.key = [[coder decodeObjectForKey:#"Key"] retain];
self.name = [[coder decodeObjectForKey:#"Name"] retain];
self.progress = [[coder decodeObjectForKey:#"Progress"] retain];
self.size = [[coder decodeObjectForKey:#"Size"] retain];
self.path = [[coder decodeObjectForKey:#"Path"] retain];
self.progressBytes = [[coder decodeObjectForKey:#"ProgressBytes"]retain];
}
return self;
}
-(void) encodeWithCoder: (NSCoder*) coder {
[coder encodeObject:self.key forKey:#"Key"];
[coder encodeObject:self.name forKey:#"Name"];
[coder encodeObject:self.progress forKey:#"Progress"];
[coder encodeObject:self.size forKey:#"Size"];
[coder encodeObject:self.path forKey:#"Path"];
[coder encodeObject:self.progressBytes forKey:#"ProgressBytes"];
}
-(void)dealloc
{
[key release];
[name release];
[size release];
[progress release];
[path release];
[progressBytes release];
[super dealloc];
}
#end
As you can see it implement NSCoding (I think so, NSObject does not conform to NSCoding). Now when I try to do something like that just to test
downloadArray = [[[NSMutableArray alloc]init]retain];
NSNumber *number = [NSNumber numberWithInt:10];
DownloadObject *object = [[DownloadObject alloc]initWithKey:number name:#"hey" progress:number size:number path:#"hey" progressBytes:number];
[downloadArray addObject:object];
[object release];
[downloadArray writeToFile:path atomically:YES];
downloadArray is a NSMutableArray. My plist read/write is fine, the path is located in the application support and when I log it show the plist path.
But it just does not write the array to the plist, any idea ?
Property list files can only store basic data types and cannot contain custom objects. You need to convert your object to an NSData object if you want it to be written to the plist. You can do this with NSKeyedArchiver, which will encode an object which conforms to the NSCoding protocol into an NSData object.
DownloadObject *object = [[DownloadObject alloc]initWithKey:number name:#"hey" progress:number size:number path:#"hey" progressBytes:number];
NSData* objData = [NSKeyedArchiver archivedDataWithRootObject:object];
[downloadArray addObject:objData];
[object release];
When you want to reconstruct your object from the NSData object, you use NSKeyedUnarchiver:
NSData* objData = [downloadArray objectAtIndex:0];
DownloadObject* object = [NSKeyedUnarchiver unarchiveObjectWithData:objData];
You also have several memory leaks in your code. In your -initWithCoder: method, you should not be using accessors to set the value of the ivars, you should just set the ivars directly, like so:
key = [[coder decodeObjectForKey:#"Key"] copy];
You are calling -retain and then using the accessor which is specified as copy, which will mean your object has a retain count of 2 and will not be released. In general you should avoid using accessors in init methods.
Also, in the code where you allocate your downloadArray object, you are calling -alloc and then -retain on the object, which will leave it with a retainCount of 2. You should re-read the Objective-C Memory Management Guidelines.
This works for me:
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:highScoreArray forKey:kHighScoreArrayKey];
[archiver finishEncoding];
[data writeToFile:[self dataFilePath] atomically:YES];
[data release];
[archiver release];
BOOL flag = false;
ObjectFileClass *obj = [yourMutableArray objectAtIndex:0];
//TO Write Data . . .
NSData* archiveData = [NSKeyedArchiver archivedDataWithRootObject:obj.title];
flag =[archiveData writeToFile:path options:NSDataWritingAtomic error:&error];
}
if (flag) {
NSLog(#"Written");
//To Read Data . . .
NSData *archiveData = [NSData dataWithContentsOfFile:path];
id yourClassInstance = [NSKeyedUnarchiver unarchiveObjectWithData:archiveData]; // choose the type of your class instance . . .
NSLog(#"%#",yourClassInstance);
}else{
NSLog(#"Not Written");
}
First I read this article
I think I should use "copy" in my programe.
Problem is using NSMutableDictionary copy it will terminate.
***** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary removeAllObjects]: mutating method sent to immutable object'**
I have no idea about "mutating method sent to immutable object".
I didn't set NSDictionary to NSMutabledictionary pointer.
Here is my code
.h file
#interface Button : NSObject {
#private
NSString* gID;
NSString* gBackColor;
NSString* gIconImage;
int gIndex;
BOOL gEnable;
BOOL gVisible;
NSString* gText;
NSMutableDictionary* gEvents;
BOOL gUseCircle;
}
#property (nonatomic,copy) NSString *ID;
#property (nonatomic,copy) NSString *BackColor;
#property (nonatomic,copy) NSString *IconImage;
#property int Index;
#property BOOL Enable;
#property BOOL Visible;
#property (nonatomic,copy) NSString *Text;
#property (nonatomic,getter=getEvents,retain) NSMutableDictionary *Events;
#property BOOL UseCircle;
#end
.m file
#implementation Button
#synthesize ID = gID;
#synthesize BackColor = gBackColor;
#synthesize IconImage = gIconImage;
#synthesize Index = gIndex;
#synthesize Enable = gEnable;
#synthesize Visible = gVisible;
#synthesize Text = gText;
#synthesize Events = gEvents;
#synthesize UseCircle = gUseCircle;
-(NSMutableDictionary*) getEvents
{
if (!gEvents)
{
gEvents = [[NSMutableDictionary alloc] initWithCapacity:20];
}
return gEvents;
}
- (id) init
{
self = [super init];
if (self != nil)
{
gID = #"";
gBackColor = #"";
gIconImage = #"";
gIndex = 0;
gText = #"";
gUseCircle = NO;
}
return self;
}
- (void) dealloc
{
[gID release];
[gBackColor release];
[gIconImage release];
[gText release];
[gEvents removeAllObjects];
[gEvents release];
gEvents = nil;
[super dealloc];
}
And implement
tBtnXML.Events = [self SplitEvents:tNode];
SplitEvents function:
-(NSMutableDictionary*) SplitEvents:(NSDictionary*)pEvents
{
NSMutableDictionary *tEvents = [[NSMutableDictionary alloc] initWithCapacity:5];
// code blabla
//.
//.
//.
[tEvents setObject:tEvent forKey:[NSNumber numberWithInt:tEventName]];
[tEvent release];
return [tEvents autorelease];
}
But I chage NSMutableDictionary* gEvents property from copy to retain , it execute normal.
Colud anyone tell me what's wrong with my code?
If my code is incorrect with dealloc,please tell me.
Thank you appriciate.
Yes, So I fixed my setter:
-(void) setEvents:(NSMutableDictionary*) pEvents
{
NSMutableDictionary* tNewDict = [pEvents mutableCopy];
[gEvents removeAllObjects];
[gEvents release];
gEvents = tNewDict;
}
This work with no error.
It helps me a lot.
But I can't vote up >"<~
So thank you Bavarious :)
In general, mutable properties should be retain instead of copy. When you declare a property as being copy, the synthesised setter method sends -copy to the object that’s being assigned to the property. In the case of mutable objects (e.g. NSMutableDictionary), sending -copy to them makes an immutable copy, effectively creating an object of immutable type (e.g. NSDictionary) instead.
So in:
tBtnXML.Events = [self SplitEvents:tNode];
the synthesised setter sends -copy to [self SplitEvents:tNode], thus creating an immutable copy of that dictionary (i.e., an NSDictionary instance), and assign it to gEvents. This is the cause of your error: gEvents is declared as NSMutableDictionary but points to an NSDictionary instead.
For the record, mutable classes usually declare a -mutableCopy method that does make a mutable copy. It is not used by declared properties, though. If you do not want to use retain, you need to implement a custom setter that uses -mutableCopy.
How can I call a method with multiple params like below with performSelectorInBackground?
Sample method:
-(void) reloadPage:(NSInteger)pageIndex firstCase:(BOOL)firstCase;
The problem is that performSelectorInBackground:withObject: takes only one object argument. One way to get around this limitation is to pass a dictionary (or array) of arguments to a "wrapper" method that deconstructs the arguments and calls your actual method:
- (void)callingMethod {
NSDictionary * args = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInteger:pageIndex], #"pageIndex",
[NSNumber numberWithBool:firstCase], #"firstCase",
nil];
[self performSelectorInBackground:#selector(reloadPageWrapper:)
withObject:args];
}
- (void)reloadPageWrapper:(NSDictionary *)args {
[self reloadPage:[[args objectForKey:#"pageIndex"] integerValue]
firstCase:[[args objectForKey:#"firstCase"] boolValue]];
}
- (void)reloadPage:(NSInteger)pageIndex firstCase:(BOOL)firstCase {
// Your code here...
}
This way you're only passing a "single" argument to the backgrounding call, but that method can construct the multiple arguments you need for the real call (which will take place on the same backgrounded thread).
I've just found this question and wasn't happy with any of the answers. In my opinion neither make good use of the tools available, and passing around arbitrary information in arrays and dictionaries generally worries me.
So, I went and wrote a small NSObject category that will invoke an arbitrary selector with a variable number of arguments:
Category Header
#interface NSObject (NxAdditions)
-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
#end
Category Implementation
#implementation NSObject (NxAdditions)
-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ...
{
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
// Setup the invocation
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
// Associate the arguments
va_list objects;
va_start(objects, object);
unsigned int objectCounter = 2;
for (id obj = object; obj != nil; obj = va_arg(objects, id))
{
[invocation setArgument:&obj atIndex:objectCounter++];
}
va_end(objects);
// Make sure to invoke on a background queue
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[backgroundQueue addOperation:operation];
}
#end
Usage
-(void)backgroundMethodWithAString:(NSString *)someString array:(NSArray *)array andDictionary:(NSDictionary *)dict
{
NSLog(#"String: %#", someString);
NSLog(#"Array: %#", array);
NSLog(#"Dict: %#", dict);
}
-(void)someOtherMethod
{
NSString *str = #"Hello world";
NSArray *arr = #[#(1337), #(42)];
NSDictionary *dict = #{#"site" : #"Stack Overflow",
#"url" : [NSURL URLWithString:#"http://stackoverflow.com"]};
[self performSelectorInBackground:#selector(backgroundMethodWithAString:array:andDictionary:)
withObjects:str, arr, dict, nil];
}
Well, I have used this:
[self performSelectorInBackground:#selector(reloadPage:)
withObject:[NSArray arrayWithObjects:pageIndex,firstCase,nil] ];
for this:
- (void) reloadPage: (NSArray *) args {
NSString *pageIndex = [args objectAtIndex:0];
NSString *firstCase = [args objectAtIndex:1];
}
with performSelectorInBackground you can only pass one argument, so make a custom object for this method to hold your data, itll be more concise than an ambiguous dictionary or array. The benefit of this is you can pass the same object around when done containing several return properties.
#import <Foundation/Foundation.h>
#interface ObjectToPassToMethod : NSObject
#property (nonatomic, strong) NSString *inputValue1;
#property (nonatomic, strong) NSArray *inputArray;
#property (nonatomic) NSInteger returnValue1;
#property (nonatomic) NSInteger returnValue2;
#end
and pass that object to your method:
ObjectToPassToMethod *obj = [[ObjectToPassToMethod alloc] init];
obj.inputArray = #[];
obj.inputValue1 = #"value";
[self performSelectorInBackground:#selector(backgroundMethod:) withObject:obj];
-(void)backgroundMethod:(ObjectToPassToMethod*)obj
{
obj.returnValue1 = 3;
obj.returnValue2 = 90;
}
make sure to clean up the object when done to prevent memory leaks