Is #autoreleasepool still required for modern iOS 8 NSOperation usage? - ios

I’ve read through Concurrency Programming Guide
In the guide the text states that GCD dispatch queues define their own #autoreleasepool pools and mentions that it’s still recommended to define one at a per dispatch level, yet for NSOperation nothing is said and the example code provided by Apple also does not show usage of the #autoreleasepool structure. The only place where #autoreleasepool is vaguely mentioned in the context of NSOperation is in the Revision History,
2012-07-17 - Removed obsolete information about autorelease pool usage with operations.
Looking at sample code available online, for ex. http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues is making usage of #autoreleasepool in the implementations of NSOperations based objects, for example:
#implementation ImageDownloader
- (void)main {
#autoreleasepool {
...
}
}
#end
How should I be implementation modern NSOperation objects?
What was the update Apple are referring to from 2012-07-17 ?

If you are deriving from NSOperation and implementing the main method, you do not need to set up an autorelease pool. The default implementation of the start method pushes an NSAutoReleasePool, calls main and then subsequently pops the NSAutoReleasePool. The same goes for NSInvocationOperation and NSBlockOperation, which share the same implementation of the start method.
The following is an abridged disassembly of the start method for NSOperation. Note the calls to NSPushAutoreleasePool, then a call to main followed by a call to NSPopAutoreleasePool:
Foundation`-[newMyObj__NSOperationInternal _start:]:
0x7fff8e5df30f: pushq %rbp
...
0x7fff8e5df49c: callq *-0x16b95bb2(%rip) ; (void *)0x00007fff8d9d30c0: objc_msgSend
0x7fff8e5df4a2: movl $0x1, %edi
; new NSAutoreleasePool is pushed here
0x7fff8e5df4a7: callq 0x7fff8e5df6d6 ; NSPushAutoreleasePool
... NSOperation main is called
0x7fff8e5df6a4: callq *-0x16b95dba(%rip) ; (void *)0x00007fff8d9d30c0: objc_msgSend
0x7fff8e5df6aa: movq %r15, %rdi
; new NSAutoreleasePool is popped here, which releases any objects added in the main method
0x7fff8e5df6ad: callq 0x7fff8e5e1408 ; NSPopAutoreleasePool
Here is a snapshot of some example code running
MyObj is allocated in the main method and I make sure the object must be autoreleased
main returns to _start, and the following image shows a stack trace with MyObj dealloc being called by the current autorelease pool, popped inside _start
For reference, this is the example code I used to verify the behavior:
#import <Foundation/Foundation.h>
#interface MyObj : NSObject
#end
#implementation MyObj
- (void)dealloc {
NSLog(#"dealloc");
}
#end
#interface TestOp : NSOperation {
MyObj *obj;
}
#end
#implementation TestOp
- (MyObj *)setMyObj:(MyObj *)o {
MyObj *old = obj;
obj = o;
return old;
}
- (void)main {
MyObj *old = [self setMyObj:[MyObj new]];
[self setMyObj:old];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
NSOperationQueue *q = [NSOperationQueue new];
TestOp *op = [TestOp new];
[q addOperation:op];
[op waitUntilFinished];
}
return 0;
}
Grand Central Dispatch similarly manages autorelease pools for dispatch queues, per the Concurrency Programming Guide:
If your block creates more than a few Objective-C objects, you might want to enclose parts of your block’s code in an #autorelease block to handle the memory management for those objects. Although GCD dispatch queues have their own autorelease pools, they make no guarantees as to when those pools are drained. If your application is memory constrained, creating your own autorelease pool allows you to free up the memory for autoreleased objects at more regular intervals.

Related

Block in MRC why this code not crash

File Person.h
#interface Person:NSObject
+ (void)callBlock:(void (^)())block;
#end
File Person.m
#imp:
+ (void)callBlock:(void (^)())block
{
[NSThread sleepForTimeInterval:2];
block();
}
#end
Code in ViewDidLoad:
Person *p = [[Person alloc] init];
void (^block)() = ^{
NSLog(#"%#",p);
[p release];
}
[Person callBlock:block];
finish!
MyQuestion:
in main function, block var is a stackblock,and this block assign to the function +callBlock:,and the block in Person.m is also a stackblock. Content of them are same.
In my opinion,the block in main will be free by system before called in Person.m, so I think this program will crash, but it run normal. why?
And i think my code is same as below.
...
void example_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%cn", b);
}];
}
void example() {
NSMutableArray *array = [NSMutableArray array];
example_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
This programe crashed! which the difference between them?
Sorry! both program use mrc!!! i did not write clearly!
As it seems, you're using manual memory management.
Therefore is an explanation:
Person object case
You create the object
You create the block
You call the block
Block logs the object out
Block frees the object
That's why there is no crash
Char log case
Since you're not using ARC, it goes this way:
You add the block that logs char
Once you leave the function that adds the block to an array, the char you've just created is going to be released from memory.
In case if you use ARC for memory management, it will keep this char alive in the memory, until this block exists. But once you remove it from an array and block's reference count equals to 0, this char also being released.
You take a block from an array
Call it, it references to a memory address of char that already released, crash appears. As explained in your error:
EXC_BAD_ACCESS (code=1, address=0x0)
Means, that you point to zero address (null pointer exception in other words).
So, that's it.
As pointed out in the comments, the first example does not crash because the block is defined in a function scope which doesn't end until after the block is called.
The second example, on the other hand, defines the block in a secondary function, which ends before the block is called. Thus, at the time of the block invocation, the stack has already been modified and the block has become invalid.
After main function executed the block in main will be free by system ,not before called in Person.m. I tried the second code in viewcontroller, it works fine, not crash.

How to handle owning references to GCD dispatch queues in malloc'ed memory

I am adapting a client Linux app (in C) that keeps a linked list of malloc(ed) structs, each of which holds a pthread (POSIX thread).
Apple strongly discourages you from using POSIX threads, so I'm adapting it to use concurrent GCD dispatch queues instead.
The source is going to be maintained across both Linux and iOS, so I'd like to limit the structural changes.
To keep the changes to a minimum, I was just going to replace a linked list of structs containing pthreads to a single malloced struct that contains a pointer to a concurrent GCD dispatch queue created with dispatch_queue_create().
However, it just occurred to me that that might not work due to ARC. By default, ARC doesn't know anything about owning references to object types in structs, especially not those that are created with malloc().
Isn't there a way to get ARC to track strong references contained in structs and/or arbitrary memory blocks? I have a vague memory of Apple adding such a thing, but can't seem to find it.
You can do it if you compile that as Objective-C++ source code.
#import <Foundation/Foundation.h>
struct Data {
dispatch_queue_t queue;
};
Objective-C
$ clang -x objective-c -fobjc-arc -c test.m
test.m:3:19: error: ARC forbids Objective-C objects in struct
dispatch_queue_t queue;
^
1 error generated.
Objective-C++
$ clang -x objective-c++ -fobjc-arc -c test.m
No errors.
Please take a look at Clang Objective-C Automatic Reference Counting document.
A program is ill-formed if it declares a member of a C struct or union to have a nontrivially ownership-qualified type.
This restriction does not apply in Objective-C++.
UPDATED
You can do it with C++ new/delete. Or if you want to use malloc, you need to call destructor of the struct.
#import <Foundation/Foundation.h>
#import <new>
#interface Test : NSObject
#property (nonatomic) dispatch_queue_t queue;
#end
#implementation Test
- (instancetype)init
{
self = [super init];
if (self)
self.queue = dispatch_queue_create("test", NULL);
return self;
}
- (void)dealloc
{
NSLog(#"Test dealloced");
}
#end
struct Data {
Test* test;
};
int main()
{
NSLog(#"stack ===================");
{
Data d;
d.test = [[Test alloc] init];
}
NSLog(#"stack end ===============");
NSLog(#"new =====================");
{
Data* d = new Data();
d->test = [[Test alloc] init];
delete d;
}
NSLog(#"new end =================");
NSLog(#"placement new ===========");
{
void* p = malloc(sizeof(Data));
Data* d = new(p) Data();
d->test = [[Test alloc] init];
d->~Data();
free(d);
}
NSLog(#"placement new end =======");
return 0;
}
result
stack ===================
Test dealloced
stack end ===============
new =====================
Test dealloced
new end =================
placement new ===========
Test dealloced
placement new end =======

why NSThread can clear autoreleased objects without creating my own autoreleasepoool

all.
i have test codes as below:
- (void)viewDidLoad
{
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(test) toTarget:self withObject:nil];
}
-(void)test
{
MyClass *obj = [[[MyClass alloc] init] autorelease];
NSLog(#"%#",[my description]);
}
i create a autorelease object in NSThread's method but no user-created autoreleasepool.
when NSThread exit,obj just dealloced(i have a breakpoint int method delloc).
why? dose NSThread create it's own autoreleasepool by itself?
I think it is that.
normally you need to create a autoreleasepoll,because you may need your thread to be always runing,if you dont create a autoreleasepoll,memory using will increase all the time.
but in your code,you just run a method in another thread, after run it, the thread exit.so the memory that used in the thread are all released.

ARC and releasing object created in method

I have stumbled upon an issue for which I can't find answer elsewhere. When I am calling a method which returns pointer to an object which is later used and at the end set to nil, it is still allocated in memory (according to Instruments). I'm using XCode 4.6.3 and iOS 6.1. ARC is turned on.
Here is sample code:
ClassA.h
#interface ClassA : NSObject
-(void)runSomething;
#end
ClassA.m
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassA
-(void)runSomething {
int counter = 0;
while (true) {
ClassB *instance = [self makeClassBWithNumber:counter];
NSLog(#"%d", [instance getNumber]);
[NSThread sleepForTimeInterval:0.01];
instance = nil;
counter++;
}
}
-(ClassB*) makeClassBWithNumber:(int)number {
return [[ClassB alloc] initWithNumber:number];
}
#end
ClassB.h
#interface ClassB : NSObject
#property int number;
-(id)initWithNumber:(int)number;
-(int)getNumber;
#end
ClassB.m
#import "ClassB.h"
#implementation ClassB
-(id)initWithNumber:(int)number {
self = [super init];
if(self) {
_number = number;
}
return self;
}
-(int)getNumber {
return [self number];
}
#end
ClassB is created in view controller and method runSomething is called. This sample code produces that created object (ClassB) is never released from memory. If I change code from
ClassB *instance = [self makeClassBWithNumber:counter];
to
ClassB *instance = [[ClassB alloc] initWithNumber:counter];
created object is properly released in each of loop cycle. What is the reason for such behaviour? I found some old answers here on stackoverflow that makeClassBWithNumber should return result invoking autorelease return [result autorelease], but this can't be done if ARC is enabled.
The difference is that +alloc returns an object with a +1 retain, which ARC will balance with a release at the end of its scope, and so immediately deallocate. +make… returns an object with a +1 retain and a matching autorelease. The autorelease pool will send a release message when it drains. Since you stay in loop "while true," the autorelease pool never drains and you accumulate memory.
The solution is to give your loop an autorelease pool:
while (true) {
#autoreleasepool { // <== Add an autorelease block here.
ClassB *instance = [self makeClassBWithNumber:counter];
//NSLog(#"%d", [instance getNumber]);
NSLog(#"%d", [instance number]); // Fix naming; do not prefix accessors with `get`
[NSThread sleepForTimeInterval:0.01];
// instance = nil; // Does nothing in this loop.
counter++;
}
}
This will cause the pool to drain on every iteration. In any case the instance=nil is unnecessary.
EDIT: Do read MartinR's answer. It gives some more details on the implementation details, and particularly why this may behave differently depending on the optimization level, and whether the called method is in the same compile unit (.m file) as the calling method. That is only an optimization detail; you still need to put this #autoreleasepool in the loop for correctness.
makeClassBWithNumber returns an autoreleased object, even with ARC.
(More precisely, it can return an autoreleased object, depending on the optimization.)
The difference to manual reference counting is that the ARC compiler inserts the autorelease call where required, not you.
From the Clang/ARC documentation:
3.2.3 Unretained return values
A method or function which returns a retainable object type but does
not return a retained value must ensure that the object is still valid
across the return boundary.
When returning from such a function or method, ARC retains the value
at the point of evaluation of the return statement, then leaves all
local scopes, and then balances out the retain while ensuring that the
value lives across the call boundary.
In the worst case, this may involve an autorelease, but callers must not assume that the value is
actually in the autorelease pool.
makeClassBWithNumber is not a alloc, copy, init, mutableCopy, or new
method and therefore returns an unretained return value.
The operative word in your question is "OLD". Old answers are no longer relevant.
This is what ARC is for.
You no longer need to worry about any memory management.
If ARC tells you not to do it... don't.
In this case, you don't need autorelease.
As others have said the difference you are seeing is down to whether a method returns and object the caller owns or an object the caller does not own.
In the former category are methods in the alloc, init, new, copy & mutableCopy categories. These all return an object owned by the caller and ARC will ensure it is released.
In the latter category are all the methods not in the first! These return an object which is not owned by the caller, ARC will ensure that this object is retained if needed and released if it was retained. Return values in this category may be in the auto-release pool, which means they will live at least as long as they are in the pool (maybe longer if they've been retained by ARC) and the standard pool is emptied at the end of each run loop cycle. This means that in loops which generate a lot of entries into the auto-release pool that a lot of no longer needed objects can accumulate in the pool - this is what you are seeing.
Under MRC the solution was to introduce a local auto-release pool, within such loops, to avoid accumulation of no longer needed objects. However under ARC this is probably not the best choice.
Under ARC the better solution is probably to follow the naming conventions, and in this case you want to use the new pattern - new is the standard pattern for alloc + init in one method. So rename your makeClassBWithNumber as newClassBWithNumber:
// *create* a new ClassB object
- (ClassB *) newClassBWithNumber:(int)number
{
return [[ClassB alloc] initWithNumber:number];
}
This indicates the method returns an object the caller owns, it is a "creation" method, and ARC will handle the rest without no longer objects accumulating.
(Adding a newWithNumber method to ClassB itself is often a good idea under ARC.)

block_copy when to use

When to copy a block? The document says, blocks are "deleted when execution returns from the scope in which they are defined.This means you can’t return them directly from a function. If blocks could only be used while their defining scope was still on the call stack, they wouldn’t be nearly as useful as they actually are"
So, here is code which I tried, hoping the block will be deleted once execution is completed in viewDidLoad.
MyReaderController.h
#interface MyReaderController : UIViewController
{
myBlockVar aBlockVar;
}
-(myBlockVar) getABlock;
#end
MyReaderController.m
#implementation MyReaderController
- (void)viewDidLoad
{
[super viewDidLoad];
aBlockVar=[self getABlock];
NSLog(#"Block Result = %f",aBlockVar(1));
}
-(void) viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSLog(#"Block Exists even after the execution completes=%# %f",aBlockVar,aBlockVar(5));
}
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
So, does this code require viewDidLoad to be changed to as coded below, if not then when should I use it.
- (void) viewDidLoad{
[super viewDidLoad];
aBlockVar=Block_copy([self getABlock]);
NSLog(#"Block Result = %f",aBlockVar(1));
}
PART 2
Later on I tried with this following code, hoping now it will return aBlockVar as nil obj in
viewDidDisappear.
- (void)viewDidLoad
{
[super viewDidLoad];
Blocker *blocker=[[Blocker alloc] init];
myBlockVar myVar=[blocker getABlock];
aBlockVar=myVar;
NSLog(#"Block Result = %f",aBlockVar(1));
blocker=nil;
myVar=nil;
}
Blocker.m
#import "Blocker.h"
#implementation Blocker
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
Are you using ARC? If so, you don't need to use Block_copy or Block_release.
If you are, then you are correct with your revised code, as Block_copy takes it off the stack and into the heap where it is has an effective retain count of 1. You would also need to call Block_release where appropriate, when finally finished with the block, to bring its balance the copy, effectively bringing the retain count back to 0.
use #property (nonatomic, copy) (int)(^myBlock)(void);
let the system do all right memory management for you!
initialize:
self.myBlock = ^int(void){
return 4.0;
};
if you want to destroy your block somewhere do self.myBlock = NULL;
An addendum to the existing answers:
Even if you're using ARC, there are certain situations where you still need Block_copy.
For example, extracting a block argument from an NSInvocation and using it after the function returns.
- (void)interceptInvocation:(NSInvocation *)call {
BlockType block;
[call getArgument:&block atIndex:2]; // ARC cannot see this happen
block = (__bridge BlockType)Block_copy((__bridge void *)block);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block();
});
}
Without the copy, the block will have been deallocated by the time it is run.
It appears a matching Block_release is not necessary, as when I added one it crashed due to too many releases.
with arc never, without arc:
: when you have a STACK block and want to keep it as a HEAP block (e.g. when you have a block in a function and want it to live after you exited the function!)
You need to block_copy it then.
also you need retain/release it as you would a NSString so (using block_copy/block_release)

Resources