I have this code :
__block NSMutableArray *subCategoriesBlock = self.subCategories ;
__block UITableView *lstSubCategoriesBlock = self.lstSubCategories;
[[AsyncRequest initRequest: onCompletedBlock:^(NSMutableArray *subcategories) {
[subCategoriesBlock addObjectsFromArray:subcategories];
[lstSubCategoriesBlock reloadData];
}]ExecuteRequest];
AsyncRequest class will send http Request on the background and then will run onCompletedBlock.
my problem:
some times the system dealloc the current view controller and then call onCompleted block, and crash on [lstSubCategoriesBlock reloadData] because it already deallocated.
what should I do , to prevent the system from executing [lstSubCategoriesBlock reloadData] when it realeasd the viewContorller ?
Use __weak so that when object is deallocated it points to nil
__block __weak NSMutableArray *subCategoriesBlock = self.subCategories ;
__block __weak UITableView *lstSubCategoriesBlock = self.lstSubCategories;
[[AsyncRequest initRequest: onCompletedBlock:^(NSMutableArray *subcategories) {
[subCategoriesBlock addObjectsFromArray:subcategories];
[lstSubCategoriesBlock reloadData];
}]ExecuteRequest];
Related
With the recent XCode update some code blocks are displaying as warnings where "Block implicitly retains 'self'"
It is my understanding that the when you create blocks it is best practice to create a weak self to keep from creating a strong reference that will not be garbage collected.
In the below example I set the myArray to self->myArray as recommended by XCode. Does this create the strong reference? Why can't I use 'weakSelf->myArray`? Attempting to do so results in this error:
Dereferencing a __weak pointer is not allowed due to possible null
value caused by race condition, assign it to strong variable first
I thought the whole point was to create weak refrences? Isn't weakSelf just a pointer to self?
Is the self-> even necessary in the below instance?
#interface SomeViewController (){
NSMutableArray * myArray;
}
#end
- (void) doSomethingInBackground {
// Do NSURLSessionTask on the background and onCompletion call mySuccessBlock.
}
- (SomeBlock) mySuccessBlock {
__block __typeof__(SomeViewController) __weak * weakSelf = self;
return ^(NSDictionary* result){
//this line is my related to my question
self->myArray = [weakSelf sortResultsAlphabetically: result];
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData]
});
};
}
Would recasting to be the correct way?
SomeViewController * strongSelf = weakSelf;
strongSelf->myArray = [weakSelf sortResultsAlphabetically: result];
The error message is right. You have to do the "weak-strong dance". You are only doing half of the dance. Pass self into the block as weak, but then immediately assign it, inside the block, to a strong reference (as in your edited "Would recasting to be the correct way?").
Here is the code
#interface ViewController ()
#property (nonatomic, strong) NSOperationQueue *queue;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_queue = [[NSOperationQueue alloc] init];
NSBlockOperation *aBlockOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation* aWeakBlockOperation = aBlockOperation;
[aBlockOperation addExecutionBlock:^{
NSLog(#"queue should still have the operation. And it does. yay!: %#", [_queue operations]); // This should print correctly. It will show the NSBlock operation correctly residing inside the NSOperationQueue
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"Now queue is empty??: %#", [_queue operations]); // This should print as being empty
NSLog(#"And a weak block is nil???: %#", aWeakBlockOperation); // This should print out **nil**
if (![aWeakBlockOperation isCancelled]) {
// Now i have no way to check the operation
}
});
}];
[_queue addOperation:aBlockOperation];
#end
[Edit]
The goal is to have a user interaction like this:
There is a tableView on screen with 5 or more cells. When ever a user click a cell, background process will perform background process that will take a while. The App will, at 3 second intervals, check to see if the user clicked on another cell. If the user clicked on another cell, I should cancel the current operation from queue, and begin processing the new one the user clicked on.
From the code above i have 2 problems i cant solve.
How do i make it so that my weak reference isnt deallocated in the dispatch_after block? The goal of putting it there is to pause the app for exactly 3 seconds. If dispatch_after is incorrect, then what code do i use there to prevent it becoming nil?
Why is it that my NSOperationQueue become empty after I call dispatch_after? Is there a way to make it not become empty?
dispatch_after schedules the block and returns immediately. So, your NSBlockOperation's executionBlock has almost no work to do — it immediately finishes and is removed from the queue. At that time, the operation is released and so the weak reference becomes nil before the dispatch_after block is called later.
If you do the dispatch_after first and schedule the operation from inside that block, it might suit your needs. You could just use sleep, but I wouldn't recommend that since you will be unnecessarily blocking a thread. See this question for more discussion on NSOperation and delays.
You can schedule operation inside the dispatch_after block and declare aBlockOperation as an instance variable/property so aWeakBlockOperation will not became nil.
But you do not need to hassle with the NSBlockOperation to achieve your goal. You can use dispatch_block_t instance variable which you would set to a new value (block with your code you need to be executed after the column is clicked) each time the column is clicked:
#implementation ViewController
{
dispatch_block_t columnBlock;
}
- (void)columnClicked
{
columnBlock = ^{ ... your code ... };
__weak dispatch_block_t weakColumnBlock = columnBlock;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_block_t colBlock = weakColumnBlock;
if (colBlock)
colBlock();
});
}
Could someone explain why following my code crash? Crash happenes inside the block in foo method.
I've got EXC_BAD_ACCESS or "error for object: double free". And I also got "-[NSObject description]: message sent to deallocated instance" when I set "Enable Zombie Objects" ON.
#interface ViewController ()
#property (nonatomic, strong) NSObject *obj;
#end
#implementation ViewController
// just adding button
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
[btn setTitle:#"test" forState:UIControlStateNormal];
btn.frame = CGRectMake(100, 100, 100, 100);
[btn addTarget:self action:#selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
// fired by button
- (void)btnAction:(id)sender {
for (int i = 0; i < 100; i++) {
[self foo];
}
}
// I want to understand this method
- (void)foo {
NSLog(#"foo");
self.obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#", [[self obj] description]); // sometimes crash happenes here with a message "-[NSObject description]: message sent to deallocated instance"
});
}
#end
Looks like self.obj is deallocated between [self obj] and [obj description]. But I'm not sure why.
I think the object from [self obj] should be owned by it's scope and should not be deallocated even if self.obj = NSObject.new is executed at the same time on other threads.
Is my understanding wrong?
I'm testing on iOS 7.0.4 with ARC. Thanks!
You have a for loop that is calling your -foo method, so self.obj is rapidly getting set to new values. Each time this happens, you're executing code asynchronously that is accessing your (nonatomic) property. But even if it is always getting a correct value for that property when being accessed from multiple threads, the main thread is very likely setting the property to a new value before the background thread finishes using the previous value of the property. And once the property gets changed to a new value, it releases the previous object that was assigned to it.
Since you're accessing your property from multiple threads, you want it to be atomic, not nonatomic, so change your property to this:
#property (strong) NSObject *obj;
atomic is the default. It is probably also safer to do the following with your asynchronous block:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSObject *obj = self.obj;
if (self.obj) {
NSLog(#"%#", [obj description]);
}
});
You should no longer see a crash if you do this, because obj will always either be nil or a valid object with a strong reference to it inside the block.
However, you probably won't get the results you expect from this. For each execution of your asynchronous block, it's not guaranteed that you'll get the subsequent instances of NSObject that you're creating. There might be times where it executes your block where obj is the same object both times, and where you never see some of the objects that were created. This is because your asynchronous block isn't getting the instance set immediately before you made the call to invoke the block, it's getting it from the property. If you want this to use the instance set immediately prior, you must do something like the following:
__block NSObject *obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#", [obj description]);
});
This should always use the instance that you created specifically for that invocation of the asynchronous block.
I suspect the issue is caused by the nonatomic property attribute as you are re-allocating self.obj 100 times I think there is a possibility of the background thread reading a partially reallocated object pointer.
Please try with:
#property (atomic, strong) NSObject *obj;
By the time the background logging is being done, self.obj could be different or in the middle of being changed.
Use a local variable like this:
- (void)foo {
NSLog(#"foo");
NSObject *val = [NSObject new];
self.obj = val;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#", val);
});
}
This will avoid threading issues and ensure the NSLog logs the proper instance.
I'm using Multipeer-Connectivity.
When the session ends, the app comes to the main menu and all network stuff is released then deallocated.
But my dealloc method is called in main thread and the MCSession object takes a very long time to release itself, I don't know why, and consequently the main menu screen freezes.
If somebody know why MCSession could be so long, I'm interested. But if it comes from the MCSession itself, is it a good solution to do this?
-(void) dealloc
{
//... other release
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_session release];
_session = nil;
});
[super dealloc];
}
EDIT: nope, it's definitely not a good solution, because it makes my app crashing. Anyway, other ideas?
When you call [_session release] since _session is an Ivar, the compiler will replace this line by [self->_session release] and the block will retain self instead of the iVar _session.
Here you have 2 problems:
Trying to retain an object(self) which are deallocating.
When the queue will be executed, it'll call self which is already deallocated.
The following solution create a local variable which point to the same address as the iVar and release it inside the block, the block will not capture self.
-(void) dealloc
{
//... other release
MCSession* localSession = _session;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[localSession release];
});
[super dealloc];
}
bsarr007's solution will work for non-ARC projects. If you are using ARC, you can try this:
__block MCSession *localSession = _session;
_session = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
oldSession = nil;
});
It works for me. What I am doing here is increasing reference count of MCSession object by creating new local variable that points to that object, so it won't be deallocated immediately when setting _session = nil. After that I am running asynchronously the code that decreasing reference counter of my MCSession object using background queue.
I have a view which has a UITableView where I am lazy loading images. I have a class called ThumbDownloader where I initialize an NSURLConnection and upon finishing loading the image when connectionDidFinishLoading is called, within connectionDidFinishLoading, I make a call like this back to my main view:
[delegate appImageDidLoad:self.indexPathInTableView];
In my main view I have an array of ThumbDownloader instances. The array is named: imageDownloadsInProgress
The problem is, if I enter the view and quickly exit it before all of the images are done downloading, I get the zombie:
-[myApp appImageDidLoad:]: message sent to deallocated instance 0xa499030
I have tried a bunch of ways to release the ThumbDownloader instances in dealloc and such, but nothing seems to work.
Here is where I set up the instance and add it to the array:
- (void)startIconDownload:(Product *)product forIndexPath:(NSIndexPath *)indexPath
{
ThumbDownloader *thumbDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (thumbDownloader == nil)
{
thumbDownloader = [[ThumbDownloader alloc] init];
thumbDownloader.product = product;
thumbDownloader.imageSizeWidth = 87;
thumbDownloader.imageSizeHeight = 87;
thumbDownloader.indexPathInTableView = indexPath;
thumbDownloader.delegate = self;
[imageDownloadsInProgress setObject:thumbDownloader forKey:indexPath];
[thumbDownloader startDownload];
[thumbDownloader release];
}
}
Make sure you clear the delegate on the NSURLConnection.
Just add
[connection cancel]
in the dealloc method of ThumbDownloader class. This will cancel the ongoing download and will prevent the message from being called.