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.
Related
I am facing a strange crash where an instance of NSNumber seems to be deallocated although it persists in array. I have created a system to download multiple files from remote server and have a block to indicate progress (an average progress really). And the computation of the progress produces a crash. The crash is not consistent and happens "usually" at can occur at any point. [_NSProgressFractionTuple floatValue]: unrecognized selector sent to instance 0x17042ab80 leads me to believe the NSNumber is somehow deallocated and I fail to see how this is possible.
To give the full method code:
- (void)downloadFilesFromURLs:(NSArray<NSString *> *)urlPaths withProgress:(void (^)(float progress))progressBlock completion:(void (^)(NSError *error))completionBlock {
NSMutableArray *toLoad = [[NSMutableArray alloc] init];
for(NSString *path in urlPaths) {
if([self fileForURL:path] == nil) {
[toLoad addObject:path];
}
}
NSInteger itemsToLoad = toLoad.count;
if(itemsToLoad <= 0) {
if(completionBlock) {
completionBlock(nil);
}
return;
}
// Set progresses to zero
__block NSMutableArray *progresses = [[NSMutableArray alloc] init];
for(int i=0; i<itemsToLoad; i++) [progresses addObject:[[NSNumber alloc] initWithFloat:.0f]];
__block NSInteger requestsOut = itemsToLoad;
__block NSError *latestError = nil;
for(int i=0; i<itemsToLoad; i++) {
NSInteger index = i;
[self downloadFileFromURL:toLoad[index] named:nil withProgress:^(float progress) {
progresses[index] = [[NSNumber alloc] initWithFloat:progress];
if(progressBlock) {
float overallProgress = .0f;
for(NSNumber *number in [progresses copy]) {
overallProgress += number.floatValue;
}
progressBlock(overallProgress/itemsToLoad);
}
} completion:^(NSString *filePath, NSError *error) {
if(error) latestError = error;
requestsOut -= 1;
if(requestsOut <= 0) {
if(completionBlock) {
completionBlock(latestError);
}
}
}];
}
}
Code explanation:
So this method accepts an array of URLs. It then checks if some of the files were already downloaded and creates a new array which only contains URLs that need to be downloaded. If all files exist or no URLs are provided then the completion is called and the operation breaks.
Next I create a mutable array and fill it with NSNumber instances all having a zero value. I remember how many requests will be made and I create a placeholder for an error. I iterate through all the URLs and initialize requests where each will report a progress and completion and both of these are on a main thread.
So in progress block I access the array of progresses to assign the new values through indexing. I then compute an average progress and report overall progress to an input block.
The request completion decreases the number of requests counter and when that one falls to zero the input completion is called.
The situation:
It all works as expected, the whole procedure is correct. The given values are all valid and all the files are there on the server and are accessible. When the app does not crash it all works as it should.
But when it crashes it crashes in
for(NSNumber *number in [progresses copy]) {
overallProgress += number.floatValue;
}
and the crash is random but in any case the number.floatValue seems to be accessing a memory that it shouldn't.
I now have a solution where I replaced the progresses array with pure C pointer float *progresses = malloc(sizeof(float)*itemsToLoad); which is freed on completion. It seems to work but still, what am I missing here? What could be the cause of array with NSNumbers not working here?
Some additional info:
Memory is OK, this is writing directly into files and even if it didn't the overall file size is relatively small
Disk space is OK
I was using #(progress) syntax but changed it to explicit allocation in hopes of removing the issue
progresses does not need __block, I added it just in case
Completion does not get called before all the progresses get called and even if it did I see no reason to crash the app
Thank you!
NSMutableArray is not thread safe. So even though there is no explicit memory management issue, if NSMutableArray is accessed at the same time by two different thread bad things can happen. I believe that dispatching the withProgress block in a serial queue would solve the issue.
This method will be called from multiple threads and returns a static dictionary which will only be read and not written to by the calling threads. The initial values are dynamic and thus the internal implementation is an NSMutableDictionary.
From what I understand, since the assignment to dictionary is not a static initialiser and its state is also further mutated, the #synchronized block is required before it's returned to ensure the initialised values are seen by all calling threads. Is this correct or should I remove the #synchronized and set a memory barrier as the last line of the dispatch_once block?
I only need to ensure that the initially assigned values are visible to all caller threads.
+ (NSDictionary *)threadSafeMethod {
static NSMutableDictionary *dictionary;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dictionary = [NSMutableDictionary dictionary];
[dictionary setValue:dynamicValue1 forKey:#"key1"];
[dictionary setValue:dynamicValue2 forKey:#"key2"];
});
#synchronized(dictionary) {
return dictionary;
}
}
#synchronized is basically a mutex on the lock object.
The way you use it protects only the return of the object and not it's use. Thus, the threads will block each other to obtain the dictionary pointer.
Update: as your object is immutable on the outside, the protection is not needed there.
I doubt that #synchronize is of any use at all in your case, just leave it as is. Initialization will be done once, others are blocked until the first in line finished the block.
Then everybody get's his pointer to the dictionary created, so all threads read the same dictionary and there is no blocking any more.
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.
I've been stuck on this for approximately two weeks. I hate posting things that have been asked a lot but I really have gone through them all.
I used Ray Wenderlich's tutorial for saving data in an iPhone app.
http://www.raywenderlich.com/tutorials
So that is the setup I have going on in my app. I'm saving very simple objects. My Card object consists of a name, type, and image. That's all. So the tutorial is quite close to mine. Which is making this more frustrating.
The thing is, I have some NSLog statements in there for loading. I have it displaying the folder it's using to load and what objects it does load. Right now it is displaying this.
Loading cards from /Users/zach/Library/Application Support/iPhone Simulator/7.0.3-64/Applications/E3DB01FD-A37E-4A69-840B-43830F2BDE2C/Library/Private Documents
2013-11-04 00:02:50.073 CardMinder[84170:a0b] ()
So it seems to be trying to load them, but there's nothing there to load. Here is my function to save data.
- (void)saveData {
if (_data == nil) return;
[self createDataPath];
NSString *dataPath = [_docPath stringByAppendingPathComponent:kDataFile];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:_data forKey:kDataKey];
[archiver finishEncoding];
NSLog(#"%#",dataPath);
NSLog(#"%#",data);
[data writeToFile:dataPath atomically:YES];
}
Which is really just what's posted in that tutorial. I know if you feel generous enough to help me out i'll have to post some more code but I don't want to flood the post with useless stuff so just let me know and i'll get it out here.
I really appreciate anyone that can help, I have recently entered the desperation state and need help.
Thanks
UPDATE
NSError *error;
[data writeToFile:dataPath options:NSDataWritingAtomic error:&error];
NSLog(#"error: %#", error.localizedFailureReason);
These are the methods for the CardData class. I'm doing the name, type, and a bool here.
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:kNameKey];
[aCoder encodeObject:_cardType forKey:kTypeKey];
[aCoder encodeBool:_checkedOut forKey:kOutKey];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSString *name = [aDecoder decodeObjectForKey:kNameKey];
NSString *cardType = [aDecoder decodeObjectForKey:kTypeKey];
BOOL checkedOut = [aDecoder decodeBoolForKey:kOutKey];
return [self initWithName:name cardType:cardType _Bool:checkedOut];
}
UPDATE 2
I just put some more NSLog statements in and I found out that when I press the "Save card" button in my app, it doesn't seem to execute the saveData function at all. I have log statements galore in that saveData function and when I click the saveCard button it doesn't show any of those logs. Why would that be happening?
This is my saveButton code.
- (IBAction)saveNewCard:(id)sender
{
NSString *cardName = self.nameField.text;
_cardDoc.data.name = cardName;
CardDoc *newCard = [[CardDoc alloc] initWithName:cardName cardType:cardTypeString _Bool:NO image:chosenIcon];
[_cardDoc saveData];
NSLog(#"Card save button pressed!");
CardViewController *cardViewController = (CardViewController *)[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
[cardViewController.cards addObject:newCard];
[self.navigationController popToRootViewControllerAnimated:YES];
}
You should use writeToFile:options:error: instead of writeToFile:atomically:; that will give you an error message that should prove helpful. (The equivalent to atomically:YES is the option constant NSDataWritingAtomic.) Make sure you're getting back a return value of YES; if not, the error should be set.
If you're getting a value of NO but the error is not set, it means you're messaging nil. A quirk of Objective-C is that messaging nil is completely valid. If the method is defined to return something, you'll even get a result: 0 or equivalent (NO, nil, etc.)
In this case, you're messaging _cardDoc. There's no return result to detect. This is a bit harder to defensively code around, but [_cardDoc saveData] is actually [nil saveData]. The debugger will just breeze past the line.
Generally, if something absolutely should not be nil, you can use NSAssert:
NSAssert(_cardData, #"_cardData should not be nil");
[_cardData saveData];
But use this sparingly; you'll probably come to usually appreciate this behaviour.
A few things.
Post the results of your log statements so we know what you are seeing.
In order for your approach to work, your _data object needs to conform to the NSCoding protocol. That means you need to add the protocol declaration to your interface, and implement the methods encodeWithCoder and initWithCoder.
In those methods you need to save all the state data for your object / load the state back into your object.
Those methods are the most likely source of problems with your code. Post those methods if you need help with them, and walk though them in the debugger.
You might also look at the NSKeyedArchvier class method archivedDataWithRootObject. That method takes an object and encodes it into an NSData object in one step. The method archiveRootObject:toFile: take it a step further, and writes the data directly to a file for you.
NSKeyedUnarchiver has the corresponding methods unarchiveObjectWithData and unarchiveObjectWithFile to recreate your object from data/a file.
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.