I have similar code in two parts of my app, xcode gives the warning for one but not the other. From my understanding of the documentation I think there should be no warning in either case.
// In one part of my code I have this
__block UploadOperation *uploadOperation = [[UploadOperation alloc] initWithPsc:self.persistentStoreCoordinator webService:self.webService];
uploadOperation.completionBlock = ^{
if (uploadOperation.uploadCount > 0) {
self.expediteNextSync = YES;
}
};
// And in another I have this
__block SyncOperation *syncOperation = [[SyncOperation alloc] initWithPsc:self.persistentStoreCoordinator webService:self.webService];
syncOperation.completionBlock = ^{
if (syncOperation.expediteNextSync) { // <--- This one gives the warning "Capturing 'syncOperation' strongly in this block is likely to lead to a retain cycle"
...
}
};
The documentation says:
In iOS 8 and later and macOS 10.10 and later, this property is set to
nil after the completion block begins executing.
So I don't think there will be any retain cycle.
Related
I am needing to use #autoreleasepool because I Am getting some high memory usage. The issue is, the objects that are taking up memory are created in one method on a background thread, then sent to a second method where they are used to update the UI.
- (void)createObjects {
TestObject = *test = [[TestObject alloc] init];
[self updateUIWithTestObject:test];
}
- (void)updateUIWithTestObject:(TestObject *)testObject {
// Update UI
self.textLabel.text = testObject.text;
}
Where would I be able to place the #autoreleasepool so that I can release the objects that I have created?
I have following code snippet:
-(void) doSomething
{
__block NSMutableArray *objArray = [[NSMutableArray alloc] initWithCapacity:0];
[self performOperationWithBlock:^(void)
{
//adding objects to objArray
.
.
//operation with objArray finished
// 1. should objArray be released here?
}];
//2. should objArray be released here?
}
Should I autorelease the objArray?
If it's an asynchronous call, it would make sense to create the NSMutableArray inside the actual block:
[self performOperationWithBlock:^(void)
{
NSMutableArray *objArray = [[NSMutableArray alloc] initWithCapacity:0];
//adding objects to objArray
.
.
//operation with objArray finished
// 1. should objArray be released here?
}];
As you won't be needing it after the block (it only makes sense for the duration of the async operation), so in the end release it after you have used it. Or, you can simply:
NSMutableArray *objArray = [NSMutableArray array];
And in this case you don't need to release it.
If it's a sync call, you should release it after the block.
Note: I am assuming you are populating the NSMutableArray before being used on the block, which means it makes sense to be created before the block starts.
Async approach:
-(void) doSomething
{
// Remove the `__block` qualifier, you want the block to `retain` it so it
// can live after the `doSomething` method is destroyed
NSMutableArray *objArray = // created with something useful
[self performOperationWithBlock:^(void)
{
// You do something with the objArray, like adding new stuff to it (you are modyfing it).
// Since you have the __block qualifier (in non-ARC it has a different meaning, than in ARC)
// Finally, you need to be a good citizen and release it.
}];
// By the the time reaches this point, the block might haven been, or not executed (it's an async call).
// With this in mind, you cannot just release the array. So you release it inside the block
// when the work is done
}
Sync Approach:
It assumes that you need the result immediately, and it makes sense when you do further work with the Array, after the block has been executed, so:
-(void) doSomething
{
// Keep `__block` keyword, you don't want the block to `retain` as you
// will release it after
__block NSMutableArray *objArray = // created with something useful
[self performOperationWithBlock:^(void)
{
// You do something with the objArray, like adding new stuff to it (you are modyfing it).
}];
// Since it's a sync call, when you reach this point, the block has been executed and you are sure
// that at least you won't be doing anything else inside the block with Array, so it's safe to release it
// Do something else with the array
// Finally release it:
[objArray release];
}
You should release it after the performOperationWithBlock: method has finished, in my opinion, provided that method is synchronous (i.e. works on the same thread as the calling thread).
If that method is asynchronous then it should be released within the block.
If you're not using ARC, you should release the array when you no longer need it. According to the comments you added and assuming the doSomething method doesn't do anything with the array outside of the block, it should be at your 1. mark.
Option 2. Release it after [self performOperationWithBlock:...]. Block will retain and release yours objArray by himself. Releasing inside block is dangerous: block can be performed twice and then objArray will be released twice, but it's should de released once. So there is only one option left: 2.
I'm getting a really weird bad access error while using dispatch async. I managed to reduce it down to this segment of code in my program.
-(void)buttonTapped:(id)sender {
__block NSArray*foo = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Foo was initially declared here but then moved it outside.
foo = [self someMethod];
[foo retain]; // bad access here. Why ?
dispatch_async(dispatch_get_main_queue(),0) {
// doesnt matter what happens here
}); });
}
-(id)someMethod
{
return [self secondMethod];
}
-(id)secondMethod
{
// was initially returning an autoreleased object from here. Changed it
// to eliminate that as source of the error.
id newThing = [[NSObject alloc] init];
return newThing;
}
The code didnt initially look like this but this is how it is right now . Including allocating a dummy NSObject .
How is it possible for foo to get released in between calls inside a dispatch async ? I dont understand how this is possible. I know its difficult to suggest whats going from just this but any debugging suggestions would be helpful. I tried turning on NSZombies but I dont get any Zombies.
You ask:
How is it possible for foo to get released in between calls inside a dispatch_async?
It shouldn't, unless someMethod or secondMethod are, themselves, doing something asynchronously which might allow the autorelease pool to be drained in the interim.
I tried turning on NSZombies but I dont get any Zombies.
If you've got zombies turned on and you're not getting a zombie, then I suspect the problem rests elsewhere. Frankly, I suspect that the root of the problem was eliminated in your process of simplifying the sample code for the purposes of the question:
A few other observations/clarifications:
You declared foo to be a NSArray, but then you're returning NSObject. I'll assume you meant it to be NSObject throughout.
You have a line of code that says:
dispatch_async(dispatch_get_main_queue(),0) {
I'll just assume that was a typo and that you intended:
dispatch_async(dispatch_get_main_queue(), ^{
The foo variable should definitely be inside the dispatch_async block. It doesn't really make sense to have a __block variable for something (a) you don't reference outside of that block for a block; and (b) for a block you're dispatching asynchronously.
The secondMethod should return an autorelease object, as you apparently originally had it. (Or you'd probably want to change secondMethod and someMethod to start with new in their names to avoid confusion and make life easier for yourself when you eventually move to ARC.)
If you retain the foo object, you'll want to also add the appropriate release. In fact, your original code sample returns a +1 object, and then retain it again, bumping it to +2, so you'd need two release calls.
Anyway, correcting for these various issues, I end up with the following, which does not generate an exception:
- (IBAction)buttonTapped:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSObject *foo = [self someMethod];
[foo retain]; // no bad access here
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"foo = %#", foo);
[foo release];
});
});
}
- (NSObject *)someMethod
{
return [self secondMethod];
}
- (NSObject *)secondMethod
{
return [[[NSObject alloc] init] autorelease];
}
Furthermore, I would suggest, especially when using manual retain and release (MRR), that you run it through the static analyzer ("Analyze" on the Xcode "Product" menu) and make sure you have a clean bill of health. (It would have pointed out some of the issues I mentioned.) It's not perfect, but it's remarkably good at identifying issues.
But, in short, the above code is fine, and if you're still getting an exception, update your question with working code that reproduces the exception.
__block BOOL myBool = NO;
__strong MyClass *ptr = self;
self.footer.defaultSelectedItem.selectionBlock = ^{
myBool = YES;
ptr = nil;
};
This works just fine when my Scheme's Build Configuration is set to Debug, but I get an EXC_BAD_ACCESS when I run with Release. The EXC_BAD_ACCESS happens on the the following line
if(selectionBlock != nil) selectionBlock();
but if I comment out all the lines inside my selectionBlock then it runs with no error. Why does this run in Debug and not Release?
FYI I'm using Automatic Reference Counting (arc)
** EDIT in response to #RobNapier **
The original code that works in debug is:
__block BOOL flag = NO;
__strong EventsView *ptr = self;
self.footer.defaultSelectedItem.selectionBlock = ^{
if(flag) return;
flag = YES;
[ptr backTUI:nil];
flag = NO;
};
There's no reason for the extra ptr here. Using self inside the block would be more correct. The block will retain self in that case. That may cause a retain loop, so it's up to you to remember to set self.selectionBlock = nil at some point before dealloc to break the retain loop.
flag may be optimized away here. It's not clear how it could possibly be useful from the above code.
I'm always very nervous about long indirection-chains in a set operation like this one:
self.footer.defaultSelectedItem.selectionBlock = ...
I would make sure that footer, and the current defaultSelectedItem can't disappear before this runs.
Simplify the problem. Make the block just log "Running block." Then add back things until it crashes.
For the life of me I could not get this to work with a block. So instead I moved to using a setter for the Event View pointer. I'm guessing that fixed the problem with my pointer by setting up an additional ARC retain. I'm still uncertain because I never saw a zombie get logged when using the block so yeah.
When in doubt, use a selector.
I am trying to do
__weak UIButton *ptr = self.backBtn;
self.footer.defaultSelectedItem.selectionBlock = ^{
[ptr sendActionsForControlEvents:UIControlEventTouchUpInside];
};
and I'm getting an infinite loop on my code anyway.
I've already referred to:
this
this
and this
With no promising result. My program still hangs and then after a minute xcode dumps out a huge loop cycle once I run out of memory. What should I do?
EDIT
I should have also pointed out that I'm using Automatic Reference Counting (ARC).
Let your block have a flag to tell it not to execute:
__block BOOL flag = NO;
.... = ^{
if (flag) return;
flag = YES;
// rest of block code here.
flag = NO;
};