Weird EXC_BAD_ACCESS crash related with setter/getter in iOS - ios

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.

Related

attempt to insert nil object from objects

I have this below error
-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[1539]
It happens sometimes I try to tap several times on screen, because code is little, so all the code is pasted below
#interface ViewController ()
#property (nonatomic,weak) NSTimer *timer;
#property (nonatomic,strong)NSMutableArray * testArray;
#property (nonatomic,strong) dispatch_queue_t queue1;
#property (nonatomic,strong) dispatch_queue_t queue2;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.testArray = [NSMutableArray array];
_queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
_queue2 = dispatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(addObjectforArray) userInfo:nil repeats:YES];
[timer fire];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_async(_queue2, ^{
NSLog(#"touchesBeganThread:%#",[NSThread currentThread]);
NSArray * testTempArray = [NSArray arrayWithArray:self.testArray];
for (UIView *view in testTempArray) {
NSLog(#"%#",view);
}
});
}
- (void)addObjectforArray{
dispatch_async(_queue1, ^{
NSLog(#"addObjectThread:%#",[NSThread currentThread]);
[self.testArray addObject:[[UIView alloc]init]];
});
}
I can not understand why this happens, if I change _queue1 to DISPATCH_QUEUE_SERIAL, it becomes normal.
How can I understand this issue? If anyone could shed some light, that would be wonderful.
There are multiple problems in your code. They can cause all sorts of bugs randomly.
UIView should be created in the main thread using dispatch_get_main_queue().
https://developer.apple.com/documentation/uikit
For the most part, use UIKit classes only from your app’s main thread or main dispatch queue. This restriction applies to classes derived from
UIResponder
or that involve manipulating your app’s user interface in any way.
The property testArray is nonatomic but being accessed in two threads. The property should be atomic. It runs fine at this moment but it is fragile. If in the future testArray mutates, the app will crash randomly.
NSArray is not thread-safe. It should be locked while accessing in multiple threads or protected by other means.
As pointed out by #Nirmalsinh, the dispatch_async is redundant (actually harmful).
I am not sure if you have heavily simplified your code or only to test something. If you are not doing long running work, you might want to use dispatch_get_main_queue() in dispatch_async. It will save you from a lot of troubles.
It seems you are inserting nil value into your array. You cannot add nil to array or dictionary.
- (void)addObjectforArray{
NSLog(#"addObjectThread:%#",[NSThread currentThread]);
UIView *view = [[UIView alloc] init];
if(view != nil)
[self.testArray addObject:view];
}
There is no required to use a queue in the method. You are already using NSTimer for same.
Try to check above. It will help you.

Retain the delegate of UIImagePickerController

I've wrote a class which gets an image from the camera. Its header is as follows:
typedef void(^ImageTakenCallback)(UIImage *image);
#interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
UIImagePickerController *picker;
ImageTakenCallback completionBlock
}
-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;
#end
As you can see, I'm trying to make something like that in client code:
[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
}];
Here is how I've implemented ImageGetter:
-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
completionBlock = [completion copy];
picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.delegate = self;
[view addSubview:picker.view];
}
- (void)imagePickerController:(UIImagePickerController *)picker_
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
[picker.view removeFromSuperview];
picker = nil;
completionBlock(image);
}
The problem is since I'm using ARC, the instance of ImageGetter is deallocated instantly after call for -requestImage..., so the weak delegate of picker becomes nil.
Which are common ways to resolve such a issue?
I can see some ways, however, none of them seems to be quite right:
retain ImageGetter from client code, for example, assign it to a strong property. The problems here are: I wont be able to release it by setting this property to nil right after I get image, because this will mean setting retain count of object to 0 while executing the method of this object. Also, I don't want unnecessary properties (well, it is not a big problem, but nevertheless).
disable ARC for ImageGetter and manually retain at start itself and release after sending image to callback.
make static manager ImageGetterManager, which will have method requestImage..., it will create ImageGetter instances, retain them, redirect the requestImage... call, get callbacks from them and release. That seems the most consistent way, but is not it a bit complex for such a little code?
So how can I build such a class?
You can handle that within the ImageGetter class by creating and releasing a "self-reference".
In a class extension in the implementation file, declare a property
#interface ImageGetter ()
#property (strong, nonatomic) id selfRef;
#end
In requestImageInView:, set self.selfRef = self to prevent deallocation.
In the completion method, set self.selfRef = nil.
Remark: Actually you can manage the retain count even with ARC:
CFRetain((__bridge CFTypeRef)(self)); // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self)); // Decreases the retain count.
But I am not sure if this is considered "good programming" with ARC or not.
Any feedback is welcome!
If this issue is introduced when switching to ARC, I should just go for option 1, and define it as a strong property.
However the behaviour is a bit different than you described for option 1: Setting the property to nil, does NOT mean the object is instantly released, it will just cause a decrement of the retaincount. ARC will handle that fine, the object will be released as soon as all referenced objects have 'released' it.
You can use the following strategy:
ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
// do stuff with taken image
[imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];
Inside your ImageGetter implementation class, create a method that you can call inside the block like this.
-(void) releaseCompletionBlock
{
completionBlock = nil;
}

Issue updating label in iOS when iterating over array

I am new to iOS programming, and I could not find an answer out there already.
In Xcode 5, I am iterating over an array, and attempting to update a label with the values as they change.
here is the .h file...
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) NSArray *currentNumber;
#property (strong, nonatomic) IBOutlet UILabel *showLabel;
- (IBAction)start;
#end
here is the main part of the .m file...
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentNumber = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", nil];
}
This is where it gets tricky...
The following works perfectly...
- (IBAction)start {
self.showLabel.text = [NSString stringWithFormat:#"new text"];
}
#end
As does this...
- (IBAction)start {
for (NSString *p in self.currentNumber) {
NSLog(#"%#", p);
sleep(3);
}
}
#end
But when I replace the NSLog with setting the .text attribute, it "fails". The timing still happens, and the label updates with the last item in the array after...
- (IBAction)start {
for (NSString *p in self.currentNumber) {
self.showLabel.text = [NSString stringWithFormat:#"%#", p];
sleep(3);
}
}
#end
And the last bit of weirdness, if I use the NSLog, and try to change the .text attribute before the "for" loop is called, the text change is ignored until AFTER the loop completes...
- (IBAction)start {
self.showLabel.text = [NSString stringWithFormat:#"5"];
for (NSString *p in self.currentNumber) {
NSLog(#"%#", p);
sleep(3);
}
}
#end
What am I missing?
(If you want to see the source files, you can get them at https://github.com/lamarrg/iterate
As you've realized, the UI will only update when the main thread is processing events. In a loop, it won't be.
There's a couple ways around this.
The simplest is to perform your loop in a background thread. There's a wrinkle, though: This will allow the user to continue to interact with your UI. And also, the UI can only be updated from the main thread.
You'll want to dispatch your work to the background, then have the background dispatch your work back to the main thread.
This sounds complicated, and it is. Thankfully, Apple added blocks and Grand Central Dispatch to Objective-C. You can use those to break down the chunks of code and make sure they're executed on the correct thread.
- (IBAction)start {
[self disableMyUI];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_NORMAL, 0), ^{
// this code will be executed "later", probably after start has returned.
// (in all cases, later should be considered "soon but not immediately.")
for (NSString *p in self.currentNumber) {
dispatch_async(dispatch_get_main_queue(),^{
// this code will be executed "later" by the main loop.
// You may have already moved on to the next thing, and even
// dispatched the next UI update.
// Don't worry; the main queue does things in order.
self.showLabel.text = [NSString stringWithFormat:#"%#", p];
});
sleep(3); // do your heavy lifting here, but keep in mind:
// you're on a background thread.
}
dispatch_async(dispatch_get_main_queue,^{
// this occurs "later," but after other all other UI events queued
// to the main queue.
[self enableMyUI];
});
}
// this line of code will run before work is complete
}
You'll have to write disableMyUI and enableMyUI; make sure they disable everything (including the back button if you're using navigation, the tab bar if you're using a tab bar controller, etc).
Another way around this is to use a NSTimer. However, if you do this you're still doing your work on the main thread. It'll work if you can split your work into predictable, small pieces, but you're better off doing it on a background thread.
One thing to keep in mind: Although you're not likely to run into problems while developing, doing heavy work on the main thread will lead to user crashes. On iOS there is a process that watches if applications are responding to events, such as drawing updates. If an application isn't responding to events in a timely fashion, it will be terminated. So living with the lack of UI updates isn't an option for you; you need to only do time consuming operations from background thread.
See also:
Programming with Objective-C: Working with Blocks
If you want to update the label periodically, don't use sleep. If you call it on the main thread you'll be blocking the UI, which is not very desirable.
Use a NSTimer instead, making it fire every N seconds.
Something like this will do:
- (void)startUpdatingLabel {
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(updateLabelWithIndex:) userInfo:#0 repeats:NO];
}
- (void)updateLabel:(NSTimer *)timer {
NSInteger index = [timer.userInfo integerValue];
if (index >= self.currentNumber.count) {
return;
}
self.showLabel.text = [NSString stringWithFormat:#"%#", self.currentNumber[index]];
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(updateLabelWithIndex:) userInfo:#(index+1) repeats:NO];
}
Every time updateLabel: is invoked it schedules a new timer which will call it again in 3 seconds. Each time the index value is increased and passed along.

iOS: Stopping a Thread / Method in the Middle of Process

I have a UITextfield and a UIButton. The user can enter, for example, search word such as "dog" or "cat" and it will trigger a method in another class that runs on a custom dispatch GCD queue to fetch the images (around 100 or so).
Everything works fine, except if the user in the midst of fetching, decides to change and enter another search word such as "cat" and then press the fetch button, I would like to be able to stop that thread / method while it is fetching the images from the previous search term.
I have thought about NSThread (something I never used before) or blocks (to get notified once the method has finished running), but the problem with blocks is, I will get notified once the method had finished doing its thing, but what I need here is to tell it to stop fetching (because the user has decided on another search and entered another search term).
Can someone please cite me with some samples, as to how we can be able to stop a loop / method while it is running on a custom GCD thread before it is finished? Thanks in advance.
I'm using NSOperationand NSOperationQueue to cluster markers on a map in the background and to cancel the operation if necessary.
The function to cluster the markers is implemented in a subclass of NSOperation:
ClusterMarker.h:
#class ClusterMarker;
#protocol ClusterMarkerDelegate <NSObject>
- (void)clusterMarkerDidFinish:(ClusterMarker *)clusterMarker;
#end
#interface ClusterMarker : NSOperation
-(id)initWithMarkers:(NSSet *)markerSet delegate:(id<ClusterMarkerDelegate>)delegate;
// the "return value"
#property (nonatomic, strong) NSSet *markerSet;
// use the delegate pattern to inform someone that the operation has finished
#property (nonatomic, weak) id<ClusterMarkerDelegate> delegate;
#end
and ClusterMarker.m:
#implementation ClusterMarker
-(id)initWithMarkers:(NSSet *)markerSet delegate:(id<ClusterMarkerDelegate>)delegate
{
if (self = [super init]) {
self.markerSet = markerSet;
self.delegate = delegate;
}
return self;
}
- (void)main {
#autoreleasepool {
if (self.isCancelled) {
return;
}
// perform some Überalgorithmus that fills self.markerSet (the "return value")
// inform the delegate that you have finished
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(clusterMarkerDidFinish:) withObject:self waitUntilDone:NO];
}
}
#end
You could use your controller to manage the queue,
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.name = #"Überalgorithmus.TheKillerApp.makemyday.com";
// make sure to have only one algorithm running
self.operationQueue.maxConcurrentOperationCount = 1;
to enqueue operations, kill previous operations and the like,
ClusterMarker *clusterMarkerOperation = [[ClusterMarker alloc] initWithMarkers:self.xmlMarkerSet delegate:self];
// this sets isCancelled in ClusterMarker to true. you might want to check that variable frequently in the algorithm
[self.operationQueue cancelAllOperations];
[self.operationQueue addOperation:clusterMarkerOperation];
and to respond to the callbacks when the operation has finished:
- (void)clusterMarkerDidFinish:(ClusterMarker *)clusterMarker
{
self.clusterMarkerSet = clusterMarker.markerSet;
GMSProjection *projection = [self.mapView projection];
for (MapMarker *m in self.clusterMarkerSet) {
m.coordinate = [projection coordinateForPoint:m.point];
}
// DebugLog(#"now clear map and refreshData: self.clusterMarkerSet.count=%d", self.clusterMarkerSet.count);
[self.mapView clear];
[self refreshDataInGMSMapView:self.mapView];
}
If I remember correctly I used this tutorial on raywenderlich.com as a starter.
I would recommend using NSOperation as it has cancel method which will cancel the current running operation.

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