I read somewhere that when we use #autoreleasepool { } blocks with ARC enabled, the compiler generates code to get the objc_autoreleasePoolPush() and objc_autoreleasePoolPop() functions called at the beginning and at the end of the block, respectively.
However, when I compile an Objective-C file with #autoreleasepool blocks, these two functions don't get called, even with -fobjc-arc. Instead, the compiler generates code to allocate a new NSAutoreleasePool object (with something equivalent to [[NSAutoreleasePool alloc] init]) at the beginning and to drain the pool (with a -drain call) at the end of the block.
So, are the objc_autoreleasePoolPush() and objc_autoreleasePoolPop() functions really supposed to be called with ARC enabled? If yes, what compiler options are missing?
objc_autoreleasePoolPush() and objc_autoreleasePoolPop() are really supposed to be called at the beginning and at the and of an #autoreleasepool block, respectively, from the code generated by the compiler, starting from OSX 10.7/ iOS 5.0. The missing compiler option is -fobjc-runtime=macosx-10.7.
And, by the way, ARC has nothing to do with all this, so that #autoreleasepool blocks make the compiler generate calls to those two functions even with ARC not enabled.
What's your deployment target set to? It may need to be OSX 10.8/iOS 6 to get the newer way of doing things.
Related
I'm quite new to the Objectivce-C and I was wondering what is the correct way of memory management in a static library without ARC.
Lets say my library has a method that returns NSString*:
- (NSString *) foo
{
...
NSString *result = [[NSString alloc] initWithString:#"bar"];
return [result autorelease];
}
So as far as I understand, since foo allocated the NSString it also needs to release it (or queue it for releasing). NSString is a return value, so the only thing I can do is to autorelease it. This creates a problem: if library is used in a command line tool, the developer needs to know that foo needs an #autoreleasepool otherwise calling foo multiple times inside main #autoreleasepool with eat up memory. This seems to me like I'm delegating memory management from library to the app which seems like a terrible thing to do. Is there a better way to do this? Or can I somehow make it obvious for the developer that foo needs an #autoreleasepool?
Autorelease pools do not deallocate objects automatically; they need to be manually drained. In Cocoa applications, this is done by the main thread's run loop, so normally most of developers don't have to do anything for that and don't know about it.
However, in any long-running functions, like command-line tool's main, or a background thread, it's the programmer's responsibility to manually drain autorelease pool periodically.
You are following the global memory management rules and that's the right thing to do. There's no need to change anything; it's not about ARC or static libraries.
Every thread requires an autoreleasepool*. If your code is called and there is no pool in place then the thread hasn't been set up properly and this is a programmer error.
Unless you created the thread (or process) this isn't your responsibility and has nothing to do with lazy memory management on your behalf.
The reason methods don't indicate that they need an autoreleasepool to be in place is because an autorelease pool always has to be in place.
*Sure you can write Objective-c that doesn't use autorelease, and about which you can reason with almost certainty will never use autorelease when internals change. Such code would only be able to use a small subset of Cocoa, if any, and would likely be pretty terrible.
I am reading a book which says (if I am not getting it wrong) wrapping some code with #autoreleasepool statement enables the ARC. First of all is this the case ?
My second concern is when I am doing some iOS example programs, although I enable ARC when creating a new project, I never see this directive being used anywhere (in the automatically generated code). Does this mean that ARC is not being used ? Any ideas/pointers is appreciated.
#autoreleasepool doesn't "enable" ARC. It's just the ARC way to use autorelease pools.
Before ARC, you used the NSAutoreleasePool class to set up an autorelease pool like this:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
[pool release];
But you're not allowed to call release when using ARC, so instead a new way to use autorelease pools was introduced:
#autoreleasepool {
// Code benefitting from a local autorelease pool.
}
#autoreleasepool blocks are more efficient than using an instance of NSAutoreleasePool directly; you can also use them even if you do not use ARC.
First off, the book is wrong. #autoreleasepool is orthogonal to ARC. You can use autoreleasepools without ARC and you can use ARC without autoreleasepools (but you shouldn't).
Secondly, the main thread of a project created using any of Xcode's default projects will have the autoreleasepool pool created for you. Any thread you create will need to create its own autorelease pool.
wrapping some code with #autoreleasepool statement enables the ARC
No. ARC is enabled by a compiler flag. #autoreleasepool is just a keyword used to create autorelease pools even if you're using ARC (because normally you would create and destroy them using alloc-init and release, respectively, but you can't send explicit release messages under ARC - that's why this keyword had been introduced.)
And if you enable ARC in Xcode/with the compiler/etc, it's enabled. Certainly, there are better solutions than the "autorelease everything" principle and that may be the cause of you not encountering this keyword in example code.
My second concern is when I am doing some iOS example programs, although I enable ARC when creating a new project, I never see this directive being used anywhere (in the automatically generated code). Does this mean that ARC is not being used ? Any ideas/pointers is appreciated.
You application does use this. Check out the main.m file in your project. You will find it there.
Coming from the world of managed memory, wondering what would be the proper way to clean up objects when using ARC.
For example: if declaring an instance variable in C#, .NET will allow the GC to pick it up once it leaves scope (method/loop body, etc)
What's the proper way to clean-up in Objective-C? Just set the reference/pointer to nil or call dealloc or will ARC detect that no external references are pointing to the instance once execution leaves scope and do the job for you?
ARC means "Automatic Reference Counting" and is just a way to let the compiler add the calls to retain/release/autorelease for you. It's not the same as GC but in most cases, you can consider that objects lifetime is automatically managed for you, like in GC.
If you want more information, you should read LLVM document on ARC
Last note: never call dealloc yourself. dealloc is the object's finalizer which is called once the ObjC runtime determines that the object reference count has reached 0. This method is only meant to be overriden by subclasses. In ARC mode, you generally don't need to do that, except if your object references non-object ivars that need to be finalized once the object itself is finalized.
will ARC detect that no external references are pointing to the
instance once execution leaves scope and do the job for you
Basically, yes, that's exactly what ARC will do. You don't need to clean up objects when you're using ARC; in fact, you can't (it stops you from trying to perform manual memory management).
You might want to consult the relevant discussion in my book:
http://www.apeth.com/iOSBook/ch12.html#_memory_management
It explains what's really happening behind the scenes (how memory is actually managed) and then goes on to describe how ARC shields you from most of it.
Note that (as I explain in the URL referenced above) it mostly isn't done by anything like garbage collection: it's done by inserting invisible explicit memory management throughout your code.
Well, in the past, iOS programmers were responsible for telling the system when they were done using an object that they allocated by sending the object a release message. That was done in accordance with a memory management system known as manual reference counting. As of Xcode 4.2, programmers no longer have to worry about this and can rely on the system to take care of releasing memory as necessary. This is done through a mechanism known as Automatic Reference Counting, or ARC for short. ARC is enabled by default when you compile new applications using Xcode 4.2 or later.
You can also disable ARC, in your Xcode interface, go to your main project (not main.h) your actual Xcode project, and select it, you should see a window in Xcode that displays the settings for your project, there will be one that says 'Objective-C Automatic Reference Counting' and it will be set to 'Yes', deactivate it (to 'No') and you shouldn't worry about the ARC, if you come from the world of data management and memory as you said, but keep in mind that it would be easier to you to keep updated to the iOS new features system, that are easier to the programmer to program, it just makes our life easier.
And now, the 'proper way to clean-up in Xcode' with ARC is with 'alloc' and 'init'.
With ARC in Xcode you do not need to worry for 'cleaning' that's the job of Xcode now, you just need to:
1) Create a variable.
2) Allocate.
3) Initialize.
That's it.
An example here:
int main (int argc, char * argv[])
{
#autoreleasepool {
Variable *myVariable;
// Create an instance of a Variable and initialize it
myVariable = [Variable alloc];
myVariable = [myVariable init];
// Set variable to 4/20
[myVariable setNumerator: 4];
[myVariable setDenominator: 20];
// Display the variable using the print method
NSLog (#"The value of myVariable is:");
[myVariable print];
}
return 0;
}
Just allocate and then initialize, yo do not need to do any thing else.
Keep in mind getters and setters.
According to this Apple page, I've read that when interacting with Cocoa on a POSIX thread that I should create a NSAutoreleasePool.
If you are making Cocoa calls outside of the Application Kit’s main
thread—for example if you create a Foundation-only application or if
you detach a thread—you need to create your own autorelease pool.
Unfortunately, using NSAutoreleasePool is disallowed in ARC.
What should I do then to guarantee that there is always an pool available for any ARC code that is autoreleased?
Thanks!
Use #autoreleasepool.
#autoreleasepool
{
// make Cocoa calls here
}
This allows the compiler to reason properly about the lifetime of objects that cross the pool's boundary, which is a requirement for ARC. (That's why you can't use NSAutoreleasePool.) As a bonus, it's also faster.
I'm using Xcode 4.3.3 and developing for iOS 5.0+. In development of an ARC iOS application, I've started using blocks as a callback mechanism for asynchronous operations. The app works fine in the simulator and on the device.
Then I ran it the profiler for the first time, and it started crashing on me nearly right away - in particular, an EXC_BAD_ACCESS when trying to invoke the first callback block.
After a little investigation, it was clear the difference in behavior was because the profiler runs in "Release mode" by default - in particular, with Optimization Level set to "Fastest, Smallest [-Os]" instead of "None [-O0]".
For example, the following code (simplified for this question) would crash when trying to execute the callbackBlock:
- (void) setCallbackBlock:(void (^)(NSString *input))block
{
callbackBlock = block;
}
- (void) invokeCallbackWithInput:(NSString *)input
{
if (callbackBlock) {
callbackBlock(input);
}
}
Debugging into it, calling setCallbackBlock with optimization level set to "None", the incoming block would be an NSStackBlock, and the callbackBlock would become an NSMallocBlock.
However, with Optimization Level "Fastest, Smallest", it remained an NSStackBlock.
Changing the setter code to use [block copy] fixes the crashing problem (based on iOS 5 blocks crash only with Release Build).
However, another related question indicates that this shouldn't be necessary with ARC - block variables are copied to the heap in ARC - Why does Objective-C block still work without copying it to the heap?
So my question: What's going on here, and why? (Also, how can both of those answers be correct...?)
Edit:
To clarify how callbackBlock is being declared - just above my #implementation where those methods are is this:
#interface MyClass ()
{
void (^callbackBlock)(NSString *input);
}
#end
So my question: What's going on here, and why? (Also, how can both of those answers be correct...?)
I actually think the answer to the other question is wrong, in that it doesn't answer that particular question about blocks in ARC. The question is about passing a stack based block from one function/method to another. The answer is about something different, which is capturing __block variables within a block. That's a different issue.
The answer to your question is in the FAQ of the Transitioning to ARC Release Notes:
Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more. You still need to use [^{} copy] when passing “down” the stack into arrayWithObjects: and other methods that do a retain.
So the way this works is that when you pass a block (in your case a block literal allocated on the stack), the compiler does not copy that block when it initializes the parameter for that call. The called function or method has the responsibility to copy that block itself if needed.
Where ARC does copy blocks automatically is when you are returning a block from a function or method. In that case, the compiler knows that it must do a copy to the heap for you, and so it does.
So your setter should be doing a block copy, even with ARC.
I hope that helps.
This is a long comment on Firoze's answer.
The document Automatic Reference Counting" section 7.5 states:
With the exception of retains done as part of initializing a __strong parameter variable or reading a __weak variable, whenever these semantics call for retaining a value of block-pointer type, it has the effect of a Block_copy. The optimizer may remove such copies when it sees that the result is used only as an argument to a call.
And this is the behavior I have seen.
So if callbackBlock is a strong instance variable then ARC should copy any stack-allocated block pass in block. I.e. the Debug version was correct and the Release version is a compiler bug.
If this is correct then you've found a compiler bug and should report it. Reporting it would not be bad either way, it should produce a definitive answer.