I Created ViewController.Here two buttons are creating & releasing the NSObject.
// This Button for creating & assign the values to the NSObject.
- (IBAction)CreateObjectBtn:(id)sender
{
[cpFileObject methodForRetain];
// Here the tempFilePath = [[NSBundle mainBundle]pathForResource:#"Innum_Enna_Thozha-VmusiQ.Com" ofType:#"mp3"];
cpFileObject.fileData =[NSData dataWithContentsOfFile:tempFilePath];
cpFileObject.filePath = tempFilePath;
}
// This Button for **release** the NSObject from memory Pool.
- (IBAction)releaseObjectBtn:(id)sender
{
[cpFileObject methodForRelease];
}
The NSObject Name - CPFile.
// This Instance Method for **Increase** the allocation of NSObject.
-(void)methodForRetain
{
if (!fileData)
{
[fileData retain];
}
}
// This Instance Method for **Decrease** the allocation of NSObject.
- (void)methodForRelease
{
[fileData release];
NSLog(#"%lu Object released ",(unsigned long)self.retainCount);
}
The Problem is occurring in Creating & Releasing is working only two or three times. After if I click the CreateObjectBtn. The Error is showing like this.
If you see your Queue from Viewcontroller2 in
[Viewcontroller2 playBtn:]
you are trying to setFileData(setting property).
Just try to log your value what you are setting and FileData.
if (!fileData)
{
[fileData retain];
}
There is nothing to retain.
You should create an object first. Your [fileData retain] should be replaced by:
if (!fileData)
{
fileData =[[NSData alloc] initWithContentsOfFile:tempFilePath];
}
try this:
- (IBAction)CreateObjectBtn:(id)sender
{
cpFileObject.fileData =[NSData dataWithContentsOfFile:tempFilePath];
cpFileObject.filePath = tempFilePath;
[cpFileObject methodForRetain]; //move it down
}
-(void)methodForRetain
{
[fileData retain]; //it doesn't matter if fileData is nil;
}
Related
I have an object like this:
typedef void (^ Completion) (Response *);
// Response class
#interface Response : NSObject {
NSDictionary * kdata;
}
- (id)initWithJson:(NSDictionary *)data;
#property (nonatomic, assign) NSDictionary * data;
#end
#implementation Response
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
- (NSDictionary *) data { return kdata; }
- (void) setData: (NSDictionary *)data { kdata = data; }
- (NSDictionary *) msg { return kdata[#"msg"]; }
#end
// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
NSDictionary * json = // get from networking function, which will always have key "msg".
Response * responseObj = [[Response alloc] initWithJson:json];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion != nil) { completion (responseObj); }
});
}
// inside caller method
[X doSomething:^(Response * response) {
NSLog (#"%#", [response msg]);
}
This code will raise error on accessing kdata[#"msg"], even though I'm sure from the debug that the object was initialised properly with a dictionary contains key "msg". When I debug the object, on the watch window, it shows me that the kdata data type keeps changing, from NSArrayM, NSSet, NSDictionary, etc. And its contents also keep changing. I even add retain keyword when calling completion ([responseObj retain]); but still produce error.
But if the code in class X is changed into like this:
// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
NSDictionary * json = // get from networking function, which will always have key "msg".
Response * responseObj = [[Response alloc] initWithJson:json];
if (completion != nil) { completion (responseObj); } // here is the change, no more switching to main thread
}
// inside caller method - no change here
[X doSomething:^(Response * response) {
NSLog (#"%#", [response msg]);
}
The code works perfectly. Why is that happened? This is built in Xcode without ARC.
EDIT: someone mentioned about the init. This is my mistake that what was written above is not exactly my code, and I copy the init method wrong. This is my init method:
- (instancetype) initWithData:(NSDictionary *)freshData {
NSParameterAssert(freshData); // make sure not nil
self = [super init];
if (self) {
kdata = freshData;
}
return self;
}
The problem is the object get's released right when you call the 'async' .
The way you declared your object is added to the autorelease pool since the control does not wait for 'async' to complete and the control return's by reaching the end of function 'doSomething' and releasing it's local objects which were added to the autorelease pool, and after that the memory location is used for other data and that's what you see confusing data.
I think by adding the __block specifier in front of your declaration you instruct the code to capture this object in following blocks strongly and release it when the block finished executing. Give it a try.
// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
NSDictionary * json = // get from networking function, which will always have key "msg".
__block Response * responseObj = [[Response alloc] initWithJson:json];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion != nil) { completion (responseObj); }
});
}
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
You need call supers init here and return self.
Start to learn basics.
I've started writing my first serious attempt at a hybrid, Cordova/Objective-C program for iOS, and I'm currently hitting some stumbling blocks regarding memory allocations. I need to get the user's Album art to display within the web view. I got the art to display successfully, but now there's a ton of memory being allocated.
Using the "Instruments" tool and comparing Generational Snapshots, I've narrowed my guilty culprits down to these methods - which I all wrote from scratch. But I'm confused - since I'm using automatic reference counting, and that I have everything in autorelease pools, that there shouldn't be any wasted memory. Funny thing is, I see no "leaks" being reported - just a heap that gets bigger, with more and more memory allocated.
I've attached some screenshots of the Instruments tool:
Here are direct links to the images since there is so much text:
http://i.imgur.com/rkc5dhA.png
http://i.imgur.com/U2esgBT.png
http://i.imgur.com/fmt3Mv4.png
Here's the contents of the "BukketHelper.M" class that I made (matches the header, no strong properties or any other definitions of any sort):
-(NSString *) convertULLToNSString:(NSNumber* )guid
{
return [NSString stringWithFormat:#"%llu", [guid unsignedLongLongValue]];
}
-(NSNumber *) convertStringToULL:(NSString *) guid
{
//get string to number
//this causes memory to not be released
unsigned long long ullvalue = strtoull([guid UTF8String], NULL, 10);
NSNumber *numberID = [[NSNumber alloc] initWithUnsignedLongLong:ullvalue];
return numberID;
}
Here's the contents of the "MediaQuery.M" class that I made (this matches the header exactly, no strong properties or other definitions):
-(MPMediaItem*) getMediaItemULL:(NSNumber*)guid
{
#autoreleasepool {
//run the query on
MPMediaQuery *query = [[MPMediaQuery alloc] init];
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:guid forProperty:MPMediaItemPropertyPersistentID]];
//get and return the item
NSArray *mediaResults = [query items];
return [mediaResults firstObject];
}
}
-(MPMediaItem*) getMediaItem:(NSString*)guid
{
#autoreleasepool {
BukketHelper* bh = [[BukketHelper alloc] init];
return [self getMediaItemULL:[bh convertStringToULL:guid]];
}
}
-(UIImage*) getMediaAlbumArtAsUIImage:(NSString*)guid withQuality:(NSNumber*)quality withLength:(NSNumber*)length subsituteImageName:(NSString* )filename
{
return [self getMediaAlbumArtFromMediaItemAsUIImage:[self getMediaItem:guid] withQuality:quality withLength:length subsituteImageName:filename];
}
-(NSString*) getMediaAlbumArtAsBase64:(NSString*)guid withQuality:(NSNumber*)quality withLength:(NSNumber*)length subsituteImageName:(NSString* )filename
{
NSString *base64 = nil;
#autoreleasepool {
UIImage* rawImage = [self getMediaAlbumArtAsUIImage:guid withQuality:quality withLength:length subsituteImageName:filename];
NSData *imageData = nil;
if (rawImage != nil)
{
#autoreleasepool {
//this causes memory to not be released
imageData = UIImageJPEGRepresentation(rawImage, [quality floatValue]);
base64 = [imageData base64EncodedStringWithOptions:0];
}
}
}
return base64;
}
-(UIImage*) getMediaAlbumArtFromMediaItemAsUIImage:(MPMediaItem*)item withQuality:(NSNumber*)quality withLength:(NSNumber*)length subsituteImageName:(NSString* )filename
{
UIImage *rawImage = nil;
#autoreleasepool {
bool successfulArt = NO;
if (item != nil)
{
MPMediaItemArtwork *albumArt = [item valueForProperty:MPMediaItemPropertyArtwork];
if (albumArt != nil) {
#autoreleasepool {
//this causes memory to not be released
rawImage = [albumArt imageWithSize:CGSizeMake([length doubleValue], [length doubleValue])];
successfulArt = YES;
}
}
}
if (successfulArt == NO)
{
rawImage = [UIImage imageNamed:filename];
}
}
return rawImage;
}
So yeah - my question is: What am I doing wrong when it comes to memory allocation and leaks? My current tests are exclusively using album art - so "UIImage imageNamed" shouldn't be the issue (from it's caching). In addition, I've read that ARC cannot release CoreGraphics objects, which could also be the problem.
I really could use some help with this! Thank you!
I ran into the same problem. I think there's a bug in the framework which doesn't release the memory. Basically there's a huge spike in allocated memory each time func imageWithSize(size: CGSize) -> UIImage? is called.
If it helps you any, I noticed that if you call this function passing in the artwork.bounds.size (instead of creating a new image size each time) then the memory gets allocated only once and calling imageWithSize again (with the same dimensions) does not reallocate new memory. This appears to only be true if you pass in the size of the original artwork image and don't return a custom size. If you need a custom size, perhaps you can then resize the UIImage on your own and not rely on this buggy method.
I have a problem with memory full with RNDecryptor (+) in a cycle "for" i call this method es:
for (int i=0; i < [datasource fileCount]; i++) {
...
datacrypto = [RNDecryptor decryptData:datacrypto withSettings:kRNCryptorAES256Settings password:passcode error:nil];
....
}
RNDecryptor allocates memory but the calls do not empty and sooner or later there is no more free memory and CRASH ... its possible dealloc +[RNDecriptor...] between calls or otherwise as a solution ??
thank you.
Here's the implementation of the method.
+ (NSData *)decryptData:(NSData *)theCipherText withSettings:(RNCryptorSettings)settings password:(NSString *)aPassword error:(NSError **)anError
{
RNDecryptor *cryptor = [[self alloc] initWithPassword:aPassword
handler:^(RNCryptor *c, NSData *d) {}];
cryptor.settings = settings;
return [self synchronousResultForCryptor:cryptor data:theCipherText error:anError];
}
It's no a singleton or is there any other branch? If not, you better implement your own singleton pattern.
I am getting a leak on the helper.offlineQueue line where I alloc a NSOperationQueue object. The problem is, I am not quite sure where to release it in this method...
+ (void)flushOfflineQueue
{
// TODO - if an item fails, after all items are shared, it should present a summary view and allow them to see which items failed/succeeded
// Check for a connection
if (![self connected])
return;
// Open list
NSMutableArray *queueList = [self getOfflineQueueList];
// Run through each item in the quietly in the background
// TODO - Is this the best behavior? Instead, should the user confirm sending these again? Maybe only if it has been X days since they were saved?
// - want to avoid a user being suprised by a post to Twitter if that happens long after they forgot they even shared it.
if (queueList != nil)
{
SHK *helper = [self currentHelper];
if (helper.offlineQueue == nil)
helper.offlineQueue = [[NSOperationQueue alloc] init];
SHKItem *item;
NSString *sharerId, *uid;
for (NSDictionary *entry in queueList)
{
item = [SHKItem itemFromDictionary:[entry objectForKey:#"item"]];
sharerId = [entry objectForKey:#"sharer"];
uid = [entry objectForKey:#"uid"];
if (item != nil && sharerId != nil)
[helper.offlineQueue addOperation:[[[SHKOfflineSharer alloc] initWithItem:item forSharer:sharerId uid:uid] autorelease]];
}
// Remove offline queue - TODO: only do this if everything was successful?
[[NSFileManager defaultManager] removeItemAtPath:[self offlineQueueListPath] error:nil];
}
}
Thanks!
I expect you should just do:
helper.offlineQueue = [[[NSOperationQueue alloc] init] autorelease];
The SHK object itself should be retaining the queue and will release it when it is done. The reference you are holding due to the alloc can be released immediately.
Analyzer keeps saying that I have a leak in the line with the * at the beginning and end, how would I fix this leak so it gets rid of the warning?
+ (void)flushOfflineQueue
{
// TODO - if an item fails, after all items are shared, it should present a summary view and allow them to see which items failed/succeeded
// Check for a connection
if (![self connected])
return;
// Open list
NSMutableArray *queueList = [self getOfflineQueueList];
// Run through each item in the quietly in the background
// TODO - Is this the best behavior? Instead, should the user confirm sending these again? Maybe only if it has been X days since they were saved?
// - want to avoid a user being suprised by a post to Twitter if that happens long after they forgot they even shared it.
if (queueList != nil)
{
SHK *helper = [self currentHelper];
if (helper.offlineQueue == nil)
***helper.offlineQueue = [[NSOperationQueue alloc] init];***
SHKItem *item;
NSString *sharerId, *uid;
for (NSDictionary *entry in queueList)
{
item = [SHKItem itemFromDictionary:[entry objectForKey:#"item"]];
sharerId = [entry objectForKey:#"sharer"];
uid = [entry objectForKey:#"uid"];
if (item != nil && sharerId != nil)
[helper.offlineQueue addOperation:[[[SHKOfflineSharer alloc] initWithItem:item forSharer:sharerId uid:uid] autorelease]];
}
// Remove offline queue - TODO: only do this if everything was successful?
[[NSFileManager defaultManager] removeItemAtPath:[self offlineQueueListPath] error:nil];
}
}
Thanks!
When you use properties they will often perform the proper memory management. In your situation you need to autorelease the class you set.
helper.offlineQueue = [[[NSOperationQueue alloc] init] autorelease];