While using Instruments ->Leaks in my app I have found that the largest memory leak is in SBJson Parser implementation file:
#implementation SBJsonStreamWriterAccumulator
#synthesize data;
- (id)init {
self = [super init];
if (self) {
data = [[NSMutableData alloc] initWithCapacity:8096u]; //HERE IS 100% LEAK
}
return self;
}
#pragma mark SBJsonStreamWriterDelegate
- (void)writer:(SBJsonStreamWriter *)writer appendBytes:(const void *)bytes length: (NSUInteger)length {
[data appendBytes:bytes length:length];
}
#end
1.How to fix this correctly and not to crash a parser?
2.And why are there so many troubles with memory leaks when working with SBJson ?
You are probably using SBJson 3.1 with a project that is not using ARC.
In ARC - this is just fine.
If your project is non arc, use SBJson 3.0, which is a non ARC version.
Read the SBJson documentation https://github.com/stig/json-framework/blob/master/NEWS.md
Because people don't bother reading documentation.
There's an open bug to add a pragma to make the files NOT COMPILE unless ARC is used. I just haven't had time to fix it yet. See: https://github.com/stig/json-framework/issues/151
(Disclaimer: I am the SBJson author.)
Related
I have come across an issue when loading different NSImages repeatedly in an application written using Automatic Reference Counting. It seems as though ARC is not releasing the image objects correctly, and instead the memory usage increases as the list is iterated until the iteration is complete, at which point the memory is freed.
I am seeing up to 2GB of memory being used through the process in some cases. There's a good discussion on SO on a very similar issue which ends up putting the process in an NSAutoReleasePool and releasing the image and draining the pool. This works for me as well if I don't use ARC, but it is not possible to make calls to these objects / methods in ARC.
Is there any way to make this work in ARC as well? It seems as though ARC should be figuring this out all by itself - which makes me think that the fact that these objects are not getting released must be a bug is OS X. (I'm running Lion with XCode 4.2.1).
The kind of code which is causing the issue looks like this:
+(BOOL)checkImage:(NSURL *)imageURL
{
NSImage *img = [[NSImage alloc] initWithContentsOfURL:imageURL];
if (!img)
return NO;
// Do some processing
return YES;
}
This method is called repeatedly in a loop (for example, 300 times). Profiling the app, the memory usage continues to increase with 7.5MB alloced for each image. If ARC is not used, the following can be done (as suggested in this topic):
+(BOOL)checkImage:(NSURL *)imageURL
{
NSAutoReleasePool *apool = [[NSAutoReleasePool alloc] init];
NSImage *img = [[NSImage alloc] initWithContentsOfURL:imageURL];
if (!img)
return NO;
// Do some processing
[img release];
[apool drain];
return YES;
}
Does anyone know how to force ARC to do the memory cleaning? For the time being, I have put the function in a file which is compiled with -fno-objc-arc passed in as a compiler flag. This works OK, but it would be nice to have ARC do it for me.
use #autoreleasepool, like so:
+(BOOL)checkImage:(NSURL *)imageURL
{
#autoreleasepool { // << push a new pool on the autotrelease pool stack
NSImage *img = [[NSImage alloc] initWithContentsOfURL:imageURL];
if (!img) {
return NO;
}
// Do some processing
} // << pushed pool popped at scope exit
return YES;
}
I'm using this very simple code from the Apple Guide:
NSMutableData *receivedData;
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
But for the line receivedData = [[NSMutableData data] retain]; Xcode gives me an error: PushController.m:72:25: ARC forbids explicit message send of 'retain'
How to deal with it? I'm using the Xcode 4.4.1
You are currently using the ARC to reference count for you. (ARC is "Automatic Reference Counting", a new feature to iOS 5). Therefore you do not need to manually retain or release. You can either remove your retain calls all together or turn off ARC by doing the following:
Click on the name of the project on the navigation view in the left side, go to Targets -> Build Phases and add -fno-objc-arc to the "compiler flags" for any relevant files.
See here for info on removing.
See here for basic info on ARC.
I solved the problem as below. The code is for Objective-C.
Whichever file you wrote a method for getting images from CIImage to CGImageRef:
CGImageRef cgImage = [_ciContext createCGImage:currentImage fromRect:[currentImage extent]];
make that file as non ARC. Go to Project -> BuildPhase -> ComplieSources -> Your File -> add "-fno-objc-arc" to your file.
If you have .pch file in your project, make the following line comment:
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC.
#endif
Go to the method which is used for creating images using the following function:
CGImageRef cgImage = [_ciContext createCGImage:currentImage fromRect:[currentImage extent]];
Declare _ciContext like this:
In the .h file, declare:
#property (strong, nonatomic) CIContext* ciContext;
In your method, create the context:
EAGLContext *myEAGLContext = [[EAGLContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES2];
_ciContext = [CIContext contextWithEAGLContext:myEAGLContext options:nil];
Use the _ciContext for creating images.
Write the following method in the same file:
-(void)dealloc
{
[super dealloc];
[EAGLContext setCurrentContext:nil];
}
Turning ARC on or off is a project level setting, if you need to have code that works in both modes you need to use
#if __has_feature(objc_arc)
//dont do a release or a retain or autorelease
#else
//do the release
#endif
I'm guessing this must be new functionality as this code fail on my iOS4 device, works fine on iOS5. I need this to work on both. I haven't moved to iOS5 yet as I still need to support iOS4, so I'm at a loss as how to workaround this ?
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{ //EXC_BAD_ACCESS
_sharedStoreManager = [[super allocWithZone:nil] init];
});
It's from https://github.com/MugunthKumar/MKStoreKit/blob/master/MKStoreManager.m
dispatch_once() is not new with iOS 5.0, it's been around since 4.0. I use it all the time in applications that target 4.0, such as in this singleton from one of my frameworks:
+ (GPUImageOpenGLESContext *)sharedImageProcessingOpenGLESContext;
{
static dispatch_once_t pred;
static GPUImageOpenGLESContext *sharedImageProcessingOpenGLESContext = nil;
dispatch_once(&pred, ^{
sharedImageProcessingOpenGLESContext = [[GPUImageOpenGLESContext alloc] init];
});
return sharedImageProcessingOpenGLESContext;
}
From Apple's documentation:
Availability
Available in iOS 4.0 and later.
I'm guessing your problem exists within the -init of your _sharedStoreManager. For example, is there a reason why you are using -allocWithZone: there?
This was caused by a bug in my code, and I have pushed a fix for it. Update your submodules. Thanks to Brad Larson for notifying me.
dispatch_once is available, and there's nothing wrong with the snippet you've posted.
I see two problems with the rest of the code, however, both stemming from line 194. First, the manager is being sent init twice: once inside the dispatch_once Block, and then after, on that line:
if(!_sharedStoreManager) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedStoreManager = [[super allocWithZone:nil] init];
});
#if TARGET_IPHONE_SIMULATOR
NSLog(#"You are running in Simulator MKStoreKit runs only on devices");
#else
/*194*/_sharedStoreManager = [[self alloc] init];
This is a bad thing to do.
Causing that, and more important, though, is that this looks like an infinite loop. Line 194 there calls +[MKStoreManager alloc], which will end up at +[MKStoreManager allocWithZone:], which calls +sharedManager again!
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedManager];
}
I wouldn't have thought that such a loop would cause EXC_BAD_ACCESS, but I recommend removing line 194; it's incorrect.
(I'd also recommend fixing the indentation of the if block.)
Hey, I'm trying to trap the QTMovie progress delegate method calls, and the delegate methods don't seem to be getting called. I'm trying to trap the conversion progress event by implementing
- (BOOL)movie:(QTMovie *)movie shouldContinueOperation:(NSString *)op withPhase:(QTMovieOperationPhase)phase atPercent:(NSNumber *)percent withAttributes:(NSDictionary *)attributes
but the method is not getting called. I've looked at apples sample code here http://developer.apple.com/library/mac/#samplecode/QTKitProgressTester/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003631 and can't seem to see very much difference between my code and their code. The file gets converted fine and shows up on my desktop and I can play it without issues. I just can't get the progress events. Any ideas? Here is my demo app that I'm using to test this with.
#import "testProjAppDelegate.h"
#import <QTKit/QTKit.h>
#implementation testProjAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
QTMovie* movie = [QTMovie movieWithFile:#"/Users/Morgan/Desktop/sample_iTunes.mov" error:nil];
if (movie)
{
[movie setDelegate:self];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], QTMovieExport,
[NSNumber numberWithInt:kQTFileType3GPP], QTMovieExportType, nil];
[movie writeToFile:#"/Users/Morgan/Desktop/test.mp4" withAttributes:dict error:nil];
NSLog(#"DONE");
}
}
- (BOOL)movie:(QTMovie *)movie shouldContinueOperation:(NSString *)op withPhase:(QTMovieOperationPhase)phase atPercent:(NSNumber *)percent withAttributes:(NSDictionary *)attributes
{
NSLog(#"PROGRESS");
return YES;
}
#end
It appears that this is not working because my app was compiled for 64 bit. I'm assuming this is a bug in the QTKit framework? I found a few other mentions of QTMovie delegates not working in 64 bit applications. I can compile my app for 32 bit though which isn't a problem. Still, this should work in 64 bit, shouldn't it?
I'm trying to use xcodebuild and OCUnit with my Continuous Integration server (TeamCity).
JetBrains offers test observer implementations for boost::test and CppUnit that format test output in a way that TeamCity can interpret. I need to do something similar for OCUnit if I want to use it.
There appears to be a SenTestObserver class in OCUnit but I'm ignorant of how exactly it should be used, and the OCUnit homepage doesn't seem to provide any documentation on the matter.
You can write your own observer by extending the SenTestObserver class and implementing the notification listeners
(void) testSuiteDidStart:(NSNotification *) aNotification
(void) testSuiteDidStop:(NSNotification *) aNotification
(void) testCaseDidStart:(NSNotification *) aNotification
(void) testCaseDidStop:(NSNotification *) aNotification
(void) testCaseDidFail:(NSNotification *) aNotification
then add an entry to the info.plist SenTestObserverClass with the name of your class.
At least in the version of OCUnit i'm familiar with SenTestObserver is equal parts useful/equal parts broken. I just skip it altogether and register for the notifications myself in my own class. (see SenTestSuiteRun.h and SenTestCaseRun.h for the defines of the notification names).
You can use the test and run properties of the notification to access the SenTestSuite and SenTestSuiteRun instances, and the run instance contains the info needed on the actual results.
I have implemented a simple Teamcity Adapter, you can view the gist here. SenTestObserver isn't exactly broken, it simply doesn't adhere to the best practices:
This is what you need to call in your Observer subclass to have it properly registered:
+(void)initialize
{
[[NSUserDefaults standardUserDefaults] setValue:#"TeamCityAdapter" forKey:#"SenTestObserverClass"];
// we need to force SenTestObserver to register us as a handler
// SenTestObserver is properly guarding against this invocation so nothing bad will hapen
// but this is required (bad design on SenTestObserver's side)...
[super initialize];
}
because SenTestObserver's initialize looks like this:
+ (void) initialize
{
if ([self class] == [SenTestObserver class]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *registeredDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
#"SenTestLog" , #"SenTestObserverClass",
nil];
[defaults registerDefaults:registeredDefaults];
[NSClassFromString ([defaults objectForKey:#"SenTestObserverClass"]) class]; // make sure default observer is loaded
}
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"SenTestObserverClass"] isEqualToString:NSStringFromClass(self)]) {
[self setCurrentObserver:self];
}
}
I hope this will help others out there looking for a teamcity adapter for OCUnit / SenTestingKit.