I am facing a problem. I am passing an object to another class by its reference & setting the value in that object. Now when I access this variable in callback handler then It is nil.
My sample code is:
Class A:
__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc]init];
[bobject getItemsWithJobId:&getListJobId onSuccess:^(NSArray *response) {
NSLog(#"job id %#",getListJobId); //It is nil, It should be **shiv**
} onFailure:^(NSError *error) {
}];
Class B:
.h
- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock;
.m
- (void)getItemsWithJobId:(NSString **)jobId onSuccess:(void (^)(NSArray *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
*jobId = #"shiv";
completedBlock([NSArray new]);
}
I am getting this jobId nil in class A in callback response. How can I get this value from class B to class A.
I will appreciate your help.
You should not pass by reference to get an updated value in the method, because the getListJobId at ClassA and ClassB do not point same address.
An Obj-C block capture the value of variables outside of its enclosing scope.
See "Blocks Can Capture Values from the Enclosing Scope" section.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
Instead of passing by reference, we can get the updated value from arguments of the block and update getListJobId in the block.
Class A:
__block NSString *getListJobId = nil;
ClassB *bobject = [[ClassB alloc] init];
[bobject getItemsWithJobId:getListJobId onSuccess:^(NSArray *response, NSString *updatedJobId) {
getListJobId = updatedJobId;
NSLog(#"job id %#", getListJobId); // job id **shiv**
} onFailure:^(NSError *error) {
}];
Class B: .h
- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock;
.m
- (void)getItemsWithJobId:(NSString *)jobId onSuccess:(void (^)(NSArray *, NSString *))completedBlock onFailure:(void (^)(NSError *))failureBlock
{
NSString *updatedJobId = #"**shiv**";
completedBlock([NSArray new], updatedJobId);
}
Taking the address of a __block variable does not always do what you expect.
In the current implementation, __block variables are initially allocated on the stack, and then "moved" to the heap upon any of the blocks that use it being moved to the heap (which is caused by the block being copied).
Therefore, the address of a __block variable changes over its lifetime. If you take the address of it, and it moves, then you will no longer be pointing to the version of the variable that everyone else is using.
Here, what is happening is that you take the address of the __block variable getListJobId while it is still on the stack. It is still on the stack at that point because it is caused to be moved to the heap by the copying of any block that uses it, but no block has been created at the point yet.
Then, a block that uses getListJobId gets copied somewhere and getListJobId gets moved to the heap. Exactly where this happens is not very clear, because ARC is allowed to insert copies of blocks in various places. Plus, the code you are showing here does not seem like your real code, because there would be no point to calling a "completion block" at the end of a method synchronously (in that case you would just return and let the caller perform the operations they want when completed). Rather, your real code probably performs an asynchronous operation, at the end of which the completion handler is called. dispatch_async and related asynchronous functions copy the blocks passed to them (which in turn copy any blocks captured, and so on).
I am guessing that in your real code, both the *jobId = #"shiv"; line and the calling of the completion block happen in the asynchronous operation. What is happening is that the creation of the asynchronous operation copies the block and causes getListJobId to be moved to the heap. So inside the asynchronous operation, getListJobId refers to the heap version of the variable. However, the *jobId = #"shiv"; writes to the stack version of the variable, because jobId is a pointer taken from the address of the variable when it was still on the stack. So you are writing to and reading from different variables.
Furthermore, what you are doing in *jobId = #"shiv"; is very dangerous, because by the time of the asynchronous operation, the stack frame of the original function call no longer exists. And writing to a variable on the stack after the stack frame is gone is undefined behavior, and you may be overwriting other unknown variables in memory. You are lucky it didn't crash.
Related
I do know that MagicalRecord will execute saveWithBlock on backend thread and execute completion on main thread, but just get confused about how to pass entity in saveWithBlock to completion block, specifically:
Event *wantToCreateEvent = nil;
Event *wantToUpdateEvent = toBeUpdatedEvent;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
wantToCreateEvent = [Event MR_createEntityInContext:localContext];
Event *localContextEvent = [wantToUpdateEvent MR_inContext:localContext];
localContextEvent.attri = #"newValue"
} completion:^(BOOL success, NSError *error) {
// Can I use wantToCreateEvent directly here?
// Is wantToUpdateEvent updated here?
}
For Entity Creation you can use 2 functions of Megical Record
+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context;
+ (id) MR_createEntity;
Note: For Saving the Entities You just have to Save the Context in which these entities are created
Save In Background
+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block;
+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion;
Save in Main Thread
+ (void) saveWithBlockAndWait:(void(^)(NSManagedObjectContext *localContext))block;
For More Understanding about CoreData With MegicalRecord I would recommend you to go through this tutorial
Understanding CoreData With Magical Record
Use the __block storage type modifier on your variables if you need them to be modified from within a block.
__block Event *wantToCreateEvent = nil;
__block Event *wantToUpdateEvent = toBeUpdatedEvent;
According to Apple's documentation found here:
Use __block Variables to Share Storage
If you need to be able to change the value of a captured variable from within a block, you can use the __block storage type modifier on the original variable declaration. This means that the variable lives in storage that is shared between the lexical scope of the original variable and any blocks declared within that scope.
I am having some sort of retention issue when I assign an object to an instance variable. It was my understanding that all local instance variables were strong so the assignment alone would be enough to make sure the object would not be destroyed as long as it was referenced by the instance variable, but this is not the case.
In the code below, I assign my instance MKRoute variable myRoute to the route from the calculate directions call. At that point myRoute exists and is valid, has steps and polyline etc. But later in my code, I attempt to access myRoute and I am getting a BAD ACCESS.
It appears to still have a memory pointer value, but the rest of the object is gone at least in the debugger. There is no polyline or steps etc. or at least not what it should be. There are no other assignments or modifications or myRoute in my code. What do I need to do to make sure that this object is not destroyed?
#implementation SomeClass
MKRoute *myRoute = nil;
.
.
.
-(void) showRoute:(MKMapItem *)origin destination:(MKMapItem*)destination
{
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
request.source = origin;
request.destination = destination;
request.requestsAlternateRoutes = NO;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"ERROR %#",error);
// Handle Error
} else {
[self drawRoute:response];
}
}];
}
-(void)drawRoute:(MKDirectionsResponse *)response
{
for (MKRoute *route in response.routes)
{
myRoute = route; // set the global route
[mapView addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
}
}
.
.
.
#end
I am happy to concede its a global variable vs an instance variable, but I still don't understand why its losing its retention, if I try STATIC or __strong on the variable declaration it still winds up assigned in the drawroute and gone later.
Globals are not a typical or recommended way to store variables, and you didn't provide any information about the code that accesses this variable and the stack you get when you crash (which are critical in accurately diagnosing).
That said, your problem is likely that you are modifying this variable on one queue while accessing it on another. calculateDirectionsWithCompletionHandler: executes its completion handler on the main thread. Is your crash happening when accessing it from some other thread? Updating pointer values is not promised to be atomic, and it is possible for you to receive a garbage pointer if you're reading and updating at the same time.
I am working on this code, which does some lengthy asyncronous operation on the net and when it finishes it triggers a completion block where some test is executed and if a variable get a certain value another lengthy operation should start immediately:
-(void) performOperation
{
void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request){
int variable=0;
// Do completion operation A
//...
//...
// Do completion operation B
//Get the variable value
if(variable>0){
[self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];
}
};
//Perform the lenhgty operation with the above completionBlock
[self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];
}
-(void) doLengthyAsynchronousOperationWithCompletionBlock: completionBlock
{
//Do some lengthy asynchronous stuff
}
With this code I get this warning from the compiler:
WARNING: Block pointer variable 'completionBlock' is uninitialized when caputerd by the block
I changed:
void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)
in:
__block void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)
but I get this other warning:
WARNING 2: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle
How can I fix this?
Thanks
Nicola
WARNING: Block pointer variable 'completionBlock' is uninitialized
when captured by the block
This happens because block variables initialized to a recursive block need __block storage.
Variables within a block are copied unless declared with __block, in which case they are passed as reference.
When a recursive block is assigned to a block variable, the creation happens before the assignment, and such creation triggers a variable copy. Given that the variable hasn't been assigned yet, the copied variable will be a bad value, and it will produce a crash when the block is ran.
But if we add __block, the block will be created with a reference to the variable instead. Then the variable will be initialized to the created block, and the block will be ready to use.
WARNING: Capturing 'completionBlock' strongly in this block is likely
to lead to a retain cycle
This happens because a block variable is a strong reference to the block, and the block is itself referencing the variable (because as we saw before, the variable has a __block so it is referenced instead copied).
So we need
A weak reference to the strong variable inside the block.
And a strong reference outside to prevent the block from being deallocated during the scope of the method where it is created.
void(^ completionBlock) (id obj, NSError *err, NSURLRequest *request);
void(^ __block __weak weakCompletionBlock) (id obj, NSError *err, NSURLRequest *request);
weakCompletionBlock = completionBlock = ^(id obj,NSError *err, NSURLRequest *request){
[self lengthyAsyncMethod:weakCompletionBlock];
};
The name doLengthyAsynchronousOperationWithCompletionBlock suggests that the method may outlive the method scope where the block is created. Given that the compiler doesn't copy a block passed as an argument, it's responsibility of this method to copy this block. If we are using this block with block aware code (eg: dispatch_async()), this happens automatically.
Had we been assigning this block to an instance variable, we would need a #property(copy) and a weak reference to self inside the block, but this is not the case, so we just use self.
I'm trying to understand why this code is leaking, using ARC:
- (IBAction)block2:(id)sender {
NSMutableString *aString = [[NSMutableString alloc] init];
void (^aBlock)() = ^{
NSMutableString __unused *anotherString = aString;
};
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:aBlock forKey:#"Key"];
}
As you can see, I put a block inside a collection (NSMutableDictionary, but it's the same if I use NSDictionary, NSArray ecc...), then the method returns and the dictionary is deallocated. The block should then be released. But, using instruments, I see a leak
"just to be sure" that the block has no other references, I added this line at the end of the method:
[dict setObject:[NSNull null] forKey:#"Key"];
same result.
I've found this post but the answers point to another problem:
Blocks inside NSMutableArray leaking (ARC)
Then, this is the magic:
If I change this line:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:aBlock forKey:#"Key"];
to:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:[aBlock copy] forKey:#"Key"];
the leak disappear.
I know that, under non-ARC, before passing a reference of a block literal, I must copy it (when declared literal, it's on the stack, so I need to copy it to the heap before passing outside the scope of the function where is declared)...but using ARC I shouldn't care about it.
Any indication?
This is happening with all versions from 5.0 to 6.1.
EDIT: I've made some tests, trying to understand if I'm doing something wrong or if there is some bug...
First: Am I reading wrong instruments informations?
I don't think, the leak is real and not my mistake. Look at this image...after executing the method 20 times:
Second: what happens if I try to do the same thing in a non arc environment?
this adds some strange behavior:
same function in NON-ARC environment:
- (IBAction)block2:(id)sender {
NSMutableString *aString = [[NSMutableString alloc] init];
void (^aBlock)() = ^{
NSMutableString __unused *anotherString = aString;
};
[aString release];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:[[aBlock copy] autorelease] forKey:#"Key"];
}
With the previous non-arc implementation, I have a leak only for the block (not for the string)
Changing the implementation to use an autorelease on the mutable string declaring solves the leak!!! I can't understand why, and I'm not sure if it could be related to the main post issue
// version without leak
- (IBAction)block2:(id)sender {
NSMutableString *aString = [[[NSMutableString alloc] init] autorelease];
void (^aBlock)() = ^{
NSMutableString __unused *anotherString = aString;
};
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:[[aBlock copy] autorelease] forKey:#"Key"];
}
CONCLUSIONS:
After various answers and further investigating, I understood some things:
1- Apple docs says that you must use [^{} copy] when you pass a block to a collection. This is because ARC doesn't add the copy itself. If you don't, the collection (array, dictionary..) sends a retain on a STACK ALLOCATED OBJECT - which does nothing. When the method ends, the block goes out of scope and becomes invalid. You will probably receive a bad access when using it. But note: this is not my case, I'm experiencing a different problem
2- the problem I'm experiencing is different: the block is over-retained (the opposite problem --> the block is still alive even when it shoulnd't be). Why?
I've found this: in my example, I'm using this code
void (^aBlock)() = ^{
NSMutableString __unused *anotherString = aString;
};
this code, under NON-ARC, stores a reference (aBlock) to the literal block. The block is allocated on the stack, so if you NSLog(#"%p", aBlock) -> you will see a stack memory address
But, this is the "strange" (I don't find any indication in Apple docs), if you use the same code under ARC and NSLog aBlock address, you will see that now it's on the HEAP!
For this reason the behavior is different (no bad access)
So, both incorrect but different behavior:
// this causes a leak
- (IBAction)block2:(id)sender {
NSMutableString *aString = [[NSMutableString alloc] init];
void (^aBlock)() = ^{
NSMutableString __unused *anotherString = aString;
};
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:aBlock forKey:#"Key"];
}
// this would cause a bad access trying to retrieve the block from the returned dictionary
- (NSMutableDictionary *)block2:(id)sender {
NSMutableString *aString = [[NSMutableString alloc] init];
return [NSMutableDictionary dictionaryWithObject:^{
NSMutableString __unused *anotherString = aString;
} forKey:#"Key"];
}
3 - about my last test under NON-ARC, I think that the release is in the wrong place. I released the string before adding the block to the dictionary with copy-autorelease.
The block automatically retains the variables referenced inside the block, but the retain message is sent at the copy moment, not at the declaration. So, If I release aString before copying the block, it's retain count goes to 0, then the block sends a retain message to the "zombie" object (with unexpected behavior, it can leak the block, crash, ecc ecc)
See this question for reference iOS 5 Blocks ARC bridged cast; it demonstrates the nightmare that are Blocks and ARC.
Typically, if you assign a block to a variable that lives beyond your current scope, the compiler will be automatically able to copy the block to the heap. This means when you fall out of scope, you still have the block hanging around. Similarly, the same goes with block paramaters. The compiler is aware that it'll need to make a copy of those parameters, and hence does so.
The issue with classes such as NSArray is that they don't usually need to copy an object to keep it correctly; typically they only retain the object. Whereas an object going out of scope is part of the language (hence it copies), keeping it within an object like NSArray is an application level operation. As such, the compiler isn't clever enough yet to determine that the block needs copying (Blocks are standard Obj-C objects after all, it thinks all it needs to do is retain it). In a similar vain, thats why any properties that hold blocks need to specify the copy keyword. The automatic synthesis of the property methods aren't aware a block is being stored, and need to be given a nudge to copy them when being set.
This demonstrates why the whole thing works when you use - copy on your block, you're doing what the compiler should be doing, but is not clever enough to do so...Apple even recommends this technique within its Transitioning to ARC documentation, see the FAQs.
Bootnote: In case you're wondering why I'm on about retaining, even when you're using ARC, is that this is what ARC does under the hood. The memory management model is still the same as before, but the onus is now on the system to manage it for us based on naming and conventions, whereas previously the onus was on the developer to manage their memory correctly. It's just that for blocks, the system isn't able to manage it as fully as it should, and hence the developer needs to step in from time to time.
Blocks begin their life on the stack for performance reasons. If they should live longer than the stack is around, they have to be copied to the heap.
In MRR, you had to do that copying yourself. ARC is doing that automatically for you if you pass a block up the stack (i.e. return it from a method). But if pass a block down the stack (for example, store it in an NSMutableDictionary or NSMutableArray), you have to copy it yourself.
This is documented in Apple's Transitioning to ARC documentation, search for "How do blocks work in ARC" inside that document.
For your Non-ARC examples (as you wrote in your conclusion), the copy of the block should happen before releasing aString, as aString is retained when the block is copied. Otherwise your code will show undefined behavior, it may even crash. Here is some code that demonstrates the problem with Non-ARC:
NSObject *object = [[NSObject alloc] init];
void (^aBlock)() = ^{
NSLog(#"%#", object);
};
[object release];
aBlock(); // undefined behavior. Crashes on my iPhone.
I'm basically implementing a fancier NSURLConnection class that downloads data from a server parses it into a dictionary, and returns an NSDictionary of the data. I'm trying add a completion block option (in addition to a delegate option), but it crashes anytime I try to store that data in another class.
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
I can NSLog that data just fine, and basically do whatever I want with it, but as soon as I try to save it into another variable it crashes with a really obscure message.
EDIT: the crash message is EXC_BAD_ACCESS, but the stack trace is 0x00000000 error: address doesn't contain a section that points to a section in a object file.
I'm calling this function in the init method of a singleton. It DOES let me save the data if I set this in the completion block.
[SingletonClass sharedInstance].contentDictionary = data
But then the app gets stuck forever because sharedInstance hasn't returned yet, so the singleton object is still nil, so sharedInstance in the completion block calls init again, over and over.
EDIT 2: The singleton code looks like this:
+ (SingletonClass*)sharedInstance {
static SingletonClass *instance;
if (!instance) {
instance = [[SingletonClass alloc] init];
}
return instance;
}
- (id)init {
self = [super init];
if (self) {
dataFetcher_ = [[DataFetcher alloc] init];
NSString *testURL = #"..."
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
[SingletonClass sharedInstance].contentDictionary = data;
}];
}
return self;
}
Like I said, this works fine but repeats the initialize code over and over until the app crashes. This only happens the first time I run the app on a device, because I cache the data returned and it doesn't crash once I have the data cached. I would like to be able to just say self.contentDictionary = data, but that crashes.
Specify a variable to be used in the block with the __block directive outside of the block:
__block NSDictionary *contentDictionary_;
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
You're invoking recursion before ever setting the "instance". (which I now see you understand from OP).
In your block, you can use the ivar or an accessor instead of
[SingletonClass sharedInstance].contentDictionary
use:
_contentDictionary = [data copy]; or self.contentDictionary=data;
assuming that the ivar backing the contentDictionary property is _contentDictionary.
It sounds like you tried self.contentDictionary and it failed? I got it to work in a test, with ARC turned, so there may be something about your dataFetcher that is affecting this. In my test dataFetcher just returns a dictionary with a single element.
Turns out the issue was with a bunch of different parts. My URL was empty sometimes, and my data fetcher would just fail immediately and call the completion block. In my completion block I hadn't included any error handling, so if the singleton class hadn't initialized, it would repeat forever. With a real URL this doesn't happen.
I still would like to figure out why it crashes when I try to assign the data to an ivar, though.