Why is this autorelease error occurring with ARC enabled? - objective-c

I wrote a small CLI program to delete specific Safari cookies for me. Functionally it's fine, but it's throwing up warnings about objects being "autoreleased with no pool in place". My project has ARC enabled, hence why I don't have any autorelease pools.
Here's my code:
// NSLog replacement from http://stackoverflow.com/a/3487392/1376063
void IFPrint (NSString *format, ...) {
va_list args;
va_start(args, format);
fputs([[[NSString alloc] initWithFormat:format arguments:args] UTF8String], stdout);
fputs("\n", stdout);
va_end(args);
}
int main(int argc, const char * argv[])
{
NSString *urlSearchString;
if (argc > 1) {
urlSearchString = [[NSString alloc] initWithUTF8String:argv[1]];
}
else {
IFPrint(#"No URL provided, quitting.");
return 1;
}
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSString *filterString = [[NSString alloc] initWithFormat:#"domain ENDSWITH '%#'", urlSearchString];
NSPredicate *filter = [NSPredicate predicateWithFormat:filterString];
NSArray *matchedCookies = [cookieStorage.cookies filteredArrayUsingPredicate:filter];
for (int i = 0; i < matchedCookies.count; i++) {
[cookieStorage deleteCookie:[matchedCookies objectAtIndex:i]];
}
IFPrint(#"Removed %li cookies", matchedCookies.count);
return 0;
}
The message I get is:
objc[15502]: Object 0x107b2bf00 of class NSThread autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
Which appears in the Xcode debugger or when running the release binary directly (slight digression: shouldn't these messages be stripped out of the "release" build?). The line that causes it seems to be:
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
Similarly, if I run it without passing an argument, I get a similar message:
objc[15630]: Object 0x100114ed0 of class __NSCFString autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
objc[15630]: Object 0x100114f80 of class __NSCFData autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
Which appears to come from the IFPrint function I'm using (however this doesn't show up when I use the IFPrint when I provide a proper argument).
I'm a bit out of my depth here, can anyone show me where (and how) I've gone wrong?

ARC still requires an autorelease pool. Methods like [NSPredicate predicateWithFormat:filterString] continue to release an autoreleased object (though you no longer need to concern yourself all that much since ARC handles it). Furthermore the internal implementation of any library method you call may create arbitrarily many autoreleased objects while running.
You should wrap your code in an autorelease pool via the #autoreleasepool mechanism.

Wrap the entire body of main with #autoreleasepool like so:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
// your code
}
}

All you need to do is add an autoreleasepool in your main.
int main(int argc, const char * argv[])
{
#autoreleasepool
{
//Your code
}
}

Related

Memory Management Objective-C

I don't understand why memory consumption increases and never gets released (the project is using ARC) when performing the following operations in my program (please bear with me, I'm at a basic level with plain C):
Simplified: somewhere in my program (AppDelegate for example) I call a macro which basically is a C function with variable parameters which calls other C functions that are returning some NSStrings.
These are defined and implemented in an Objective-C style class and are used together with a singleton object.
Header:
#interface MyClass : NSObject
#end
void func_1(aTypeDef paramType, NSString *input, ...);
void* func_2(NSString *arg1, NSString *arg2, NSString *arg3);
NSString* string_func_1 (void);
NSString* string_func_2 (int anInt);
NSString* string_func_3 (const char *aString);
#define F2_MACRO func_2( \
string_func_1(), \
string_func_2(anINT), \
string_func_3(aSTRING), \
)
#define F1_MACRO(input, ...) func_1(A_TYPE, input, ##__VA_ARGS__, F2_MACRO)
Implementation:
#import "MyClass.h"
static NSString *STRING_1;
static NSString *STRING_2;
static NSString *STRING_3;
#implementation MyClass
void func_1(aTypeDef paramType, NSString *input, ...) {
va_list args;
va_start(args, input);
NSString *output = [[NSString alloc] initWithFormat:input arguments:args];
fputs([output UTF8String], stdout);
va_end(args);
}
void* func_2(NSString *arg1, NSString *arg2, NSString *arg3) {
STRING_1 = arg1;
STRING_2 = arg2;
STRING_3 = arg3;
return NULL;
}
NSString* string_func_1 (void) {
return [NSString stringWithFormat:#"aString"];
}
NSString* string_func_2 (int anInt) {
return [NSString stringWithFormat:#"%d",anInt];
}
NSString* string_func_3 (const char *aString) {
return [NSString stringWithUTF8String:aString];
}
#end
Every time I call the F1_MACRO() in another Objective-C class like AppDelegate memory usage increases every time the string_func_1, string_func_2, string_func_3 return.
I'm sure that my logic and implementation are flawed and I'll appreciate any help.
func_1() is creating a non-autoreleased object.
• If you are using Automatic Reference Counting (ARC), not autoreleasing the object is fine, however it can still lead to apparent memory accretion. Specifically, if you don't have an explicitly #autoreleasepool{} or are not running an event loop on the thread that is calling that function, then the autoreleased object will never be released.
• If you aren't using ARC, then that is a straight up leak. Add [output release]; at the end of the func_1() function.

Why does the position of #autoreleasepool matter?

I'm having trouble understanding how #autoreleasepool work. Consider the following example in which I am creating an AVAssetReader for an audiofile. To make the memory impact matter, I repeated this step 1000 times.
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
void memoryTest() {
NSURL *url = [[NSURL alloc] initWithString:#"path-to-mp3-file"];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:NULL];
}
int main(int argc, const char * argv[]) {
// Breakpoint here (A)
#autoreleasepool {
for(int i = 0; i < 1000; i++) {
memoryTest();
}
}
// Breakpoint here (B)
return 0;
}
Before and after the loop at breakpoint A and breakpoint B I took a look at the memory usage of my application in the Xcode Debug Navigation. At point A my App consumes about 1.5MB of memory. At point B it is about 80MB. Interestingly the memory usage at B drops to about 4MB when I put the autoreleasepool inside the loop like so:
for(int i = 0; i < 1000; i++) {
#autoreleasepool { memoryTest(); }
}
Why does the position matter? (The breakpoint - in both cases - is outside of the autoreleasepool!) In either case the consumed memory at point B is proportional to the number of loops. Am I missing something else here to free up its memory?
I thought of the memory chart in the navigator to be delayed, but adding usleep just before the breakpoint does not change anything. If I change memoryTest() to
void memoryTest() {
NSURL *url = [NSURL URLWithString:#"path-to-mp3-file"];
}
the position of #autoreleasepool does not matter. Isn't that weird?
I am using Xcode 6, OS X SDK 10.10 with ARC enabled.
Temporary memory is being created and autorelease'ed by the code inside the loop by your code and the API calls it makes. This memory is not released until the pool is drained. The usual point it is is drained is in the run loop. But your loop does not allow the run loop to execute so by placing the #autoreleasepool inside the loop the pool can drain as your code runs.

Why unloaded class is still accessible in Cocoa?

I loaded a class dynamically with [NSBundle load]. And unloaded it dynamically with [NSBundle unload]. Anyway it looks the class is still alive after unloading.
My code is:
// In separated bundle.
#implementation EEExampleBundle
+ (void)test
{
NSLog(#"TTTTT");
}
#end
// In executable file.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool
{
id EEExampleBundle = nil;
#autoreleasepool
{
NSString* path = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"EEExampleBundle.framework"];
NSBundle* sampleBundle = [NSBundle bundleWithPath:path];
[sampleBundle load];
EEExampleBundle = (id)[sampleBundle classNamed:#"EEExampleBundle"];
[EEExampleBundle test];
BOOL r = [sampleBundle unload];
NSLog(#"unload result = %d", r);
}
[EEExampleBundle test];
}
return 0;
}
The output is:
2011-09-25 01:08:52.713 driver[2248:707] TTTTT
2011-09-25 01:08:52.714 driver[2248:707] unload result = 1
2011-09-25 01:08:52.716 driver[2248:707] TTTTT
Why the class code is still working? Is this normal? Or should I do any extra step to unload the code completely?
P.S
I'm not using ARC. I turned it off explicitly.
(more of a comment than an answer, nevertheless:) That's due to the inner #autoreleasepool block, no? You won't be able to create a new instance from your bundle, but you do keep the ones already created alive (else, that'd generate fancy bugs).

CLANG Pass-By-Value Warning?

The following code compiles fine ...
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// VARIABLES
MDD *MDD_structure;
NSString *mddFile = #"/Users/Gary/Code/Mdd/xTest.mdd";
NSFileHandle *inFile;
NSData *inBuffer;
int MDD_fCount;
int MDD_vCount;
// OPEN FILE ON DISK
inFile = [NSFileHandle fileHandleForReadingAtPath:mddFile];
if(inFile == nil) NSLog(#"FILE: Open ... ERROR");
else NSLog(#"FILE: Open ... OK");
// READ FRAME COUNT
inBuffer = [inFile readDataOfLength:sizeof(int)];
[inBuffer getBytes:&MDD_fCount length:sizeof(int)];
MDD_fCount = CFSwapInt32BigToHost(MDD_fCount);
NSLog(#"FC: %d", MDD_fCount);
But when I run it through the static analyzer "CLANG LLVM 1.0" I get the following ...
warning: Pass-by-value argument in function call is undefined.
MDD_fCount = CFSwapInt32BigToHost(MDD_fCount);
^ ~~~~~~~~~~
1 diagnostic generated.
Can anyone tell me what I am missing?
gary
You're getting an error because clang isn't convinced that simply passing the address of your variable to a function is the same as giving it a value. You could probably initialize MDD_fCount to 0 to start with to get rid of the error.
It means that you haven't initialized MDD_fCount. See this blog post and this other question for additional info.

Very basic Objective-C/C Problem

Here's my code:
#import <Foundation/Foundation.h>
void PrintPathInfo() {
const char *path = [#"~" fileSystemRepresentation];
NSLog(#"My home folder is at '%#'", path);
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
PrintPathInfo();
[pool drain];
return 0;
}
And here's my problem:
Program received signal: “EXC_BAD_ACCESS”.
I really think the problem is my NSLog but I don't know how to solve it.
Could someone help me please? Thanks!
path is not an NSString, which is why that crashes. %# in a formatting string expects an object, and asks it for a description to get a string to print... because you are using a C style string, you need to use the standard C string formatters OR convert the const char * back to an NSString using the initWithCString:encoding: class method of NSString.
Staying with a const char *, you can use:
NSLog(#"My home folder is at '%s'", path);
which would work.
%# is for objects. (Like NSString). for const char* you will want the good old %s from the c's printf format codes.
See http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html
For the format specifies and their meanings