Clarifications needed for a crash using NSArray, blocks and Manual Reference Counting - objective-c

I need some clarifications on a crash I'm encountering using NSArray, blocks and Manual Reference Counting. My goal is to store blocks on a collection (NSArray in this case) in order to reuse them in the future.
I've setup a small sample to replicate the issue. In particular, I have a class Item that looks like the following:
#import <Foundation/Foundation.h>
typedef void(^MyBlock)();
#interface Item : NSObject
- (instancetype)initWithBlocks:(NSArray*)blocks;
#end
#import "Item.h"
#interface Item ()
#property (nonatomic, strong) NSArray *blocks;
#end
#implementation Item
- (instancetype)initWithBlocks:(NSArray*)blocks
{
self = [super init];
if (self) {
NSMutableArray *temp = [NSMutableArray array];
for (MyBlock block in blocks) {
[temp addObject:[[block copy] autorelease]];
}
_blocks = [temp copy];
}
return self;
}
The usage is described below (I'm using in the app delegate).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
__block typeof(self) weakSelf = self;
MyBlock myBlock1 = ^() {
[weakSelf doSomething1];
};
MyBlock myBlock2 = ^() {
[weakSelf doSomething1];
};
NSArray *blocks = #[myBlock1, myBlock2];
// As MartinR suggested the code crashes even
// if the following line is commented
Item *item = [[Item alloc] initWithBlocks:blocks];
}
If I run the app, it crashes with an EXC_BAD_INSTRUCTION (note that I've already enabled All Exceptions breakpoints). In particular, the app stops in the main.
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}
Note: As suggested by Ken Thomases, if you use bt command on llvm console, you are to see the back trace. In this case it shows the following:
-[__NSArrayI dealloc]
If I comment the [weakSelf doSomethingX]; it works without crashes (it does not mean that is correct).
Modifying the code a little bit like the following, all runs ok.
// Item does not do anymore the copy/autorelease dance
// since used in the declaration of the blocks
- (instancetype)initWithBlocks:(NSArray*)blocks
{
self = [super init];
if (self) {
_blocks = [blocks retain];
}
return self;
}
and
__block typeof(self) weakSelf = self;
MyBlock myBlock1 = [[^() {
[weakSelf doSomething1];
} copy] autorelease];
MyBlock myBlock2 = [[^() {
[weakSelf doSomething1];
} copy] autorelease];
NSArray *blocks = #[myBlock1, myBlock2];
Item *item = [[Item alloc] initWithBlocks:blocks];
What is the point here? I think I'm missing something but I don't know what.
Update 1
Ok. I'll try to recap my thoughts based on the comments with #Martin R and #Ken Thomases.
A block, by default, is created on stack if a copy message is not sent to it (ARC does this for us) in order to move it on the heap. So, the situation in this case is the following. I create an autorelease array and I add two blocks where retain is called in a implicit manner. When the applicationDidFinishLaunching method finishes is execution, the blocks, since created on the stack (they are automatic variables) disappear. In a later moment, the array called blocks will be released since has been marked as autorelease. So, it will crash since it will send a release object to blocks that do not exist anymore.
So, my question is the following: What does it mean to send a retain message to a block that is on the stack? Why the array is the source of the crash (see the back trace)? In other words, since a block is on the stack, will it bump the retain count of it? And when it goes out of scope? In addiction, why if I comment the [weakSelf doSomething1] line the code works without problems? Not very clear to me this part.

You are sticking an object from the stack into an autoreleased array. BOOM ensues.
Consider:
typedef void(^MyBlock)();
int main(int argc, char *argv[]) {
#autoreleasepool {
NSObject *o = [NSObject new];
MyBlock myBlock1 = ^() {
[o doSomething1];
};
NSLog(#"o %p", o);
NSLog(#"b %p", myBlock1);
NSLog(#"b retain %p", [myBlock1 retain]);
NSLog(#"b copy %p", [myBlock1 copy]);
NSLog(#"s %p", ^{});
sleep(1000000);
}
}
Compiled/run as -i386 (because the #s are smaller and more obvious):
a.out[11729:555819] o 0x7b6510f0
a.out[11729:555819] b 0xbff2dc30
a.out[11729:555819] b retain 0xbff2dc30
a.out[11729:555819] b copy 0x7b6511a0
a.out[11748:572916] s 0x67048
Since the object is at 0x7b, we can assume that is the heap. 0xb is really high memory and, thus, the stack.
The retain doesn't cause a copy (because doing so would have invariably led to leaks) and retain on a stack based object is meaningless.
If you change the [o doSomething1]; to [nil doSomething1]; then that becomes a static block and that lives in readonly mapped memory (readonly-executable pages from the mach-o's TEXT segment) and, thus, there is no allocation to deallocate and retain/release/autorelease are no-ops.
As you can see, the static block ended up around 0x67048 (this number may change from run to run, btw, for a variety of reasons. Low in memory.
In fact, because of the sleep(), we can run vmmap against the a.out process and see:
==== Writable regions for process 11772
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
__DATA 00067000-00068000 [ 4K] rw-/rwx SM=ZER /tmp/a.out
That is, the static block was in the first 4K segment of mapped writable regions from the mach-o file. Note that this doesn't mean the code is in that writable region (SECURITY HOLE if it were). The code is in the TEXT segment mapped into the readable regions.

Related

Objective-c block lifetime ARC

I'm confused about the lifetime of a block under ARC. I've written a unit test to demonstrate what is confusing me.
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
CompletionBlock testBlock = ^{ NSLog(#"%#", testObject); };
XCTAssertNotNil(testObject, #"testObject should not be nil");
__weak NSObject *weakTestObject = testObject;
#autoreleasepool {
testObject = nil;
}
XCTAssertNotNil(weakTestObject, #"testObject should be retained by testBlock");
#autoreleasepool {
testBlock = nil;
}
//THIS IS THE FAILING TEST CASE
XCTAssertNil(weakTestObject, #"testObject should have been released when testBlock was released");
}
I'm guessing that this behavior has something to do with how blocks are stored on the stack/heap.
Update!
Setting the block to nil doesn't release the block as I expected because it is on the stack and will not be released until it goes out of scope. Forcing the block to go out of scope fixes my test. Updated code below.
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
__weak NSObject *weakTestObject = testObject;
#autoreleasepool {
CompletionBlock testBlock = ^{ NSLog(#"%#", testObject); };
XCTAssertNotNil(testBlock, #"testBlock should not be nil");
XCTAssertNotNil(testObject, #"testObject should not be nil");
testObject = nil;
XCTAssertNotNil(weakTestObject, #"testObject should be retained by testBlock");
//testBlock goes out of scope here
}
XCTAssertNil(weakTestObject, #"testObject should have been released when testBlock was released");
}
Blocks are created on stack, and only become destroyed when scope exits, pretty much like C++ stack-allocated objects with destructors. These blocks are exempt from reference counting and will ignore retain/release messages. Only after being copied (via Block_copy() function or copy message) they become normal heap-allocated objects that can be retained and released.
In your example, the assert shall start working if you wrap all the code before it in extra curly brackets { }, so that the assert executes after block variable's scope ends.

Static NSMutableDictionary is "just leaking"

Here's a simple class:
#import "One.h"
#import "Two.h"
#implementation DataFileRegistrar
static NSMutableDictionary *elementToClassMapping;
+ (void)load
{
[self registerClass:[One class] forElement:#"one"];
[self registerClass:[Two class] forElement:#"two"];
}
+ (void)registerClass:(Class)class forElement:(NSString *)element
{
if (!elementToClassMapping) {
elementToClassMapping = [NSMutableDictionary dictionaryWithObject:class forKey:element];
} else {
[elementToClassMapping setValue:class forKey:element];
}
}
+ (id)classForElement:(NSString *)element
{
return [elementToClassMapping valueForKey:element];
}
#end
The problem is this compiler message:
objc[7172]: Object 0x6840720 of class __NSCFDictionary autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
Any ideas what's going on?
Basically, I want to have a simple class with a couple of class methods and one static dictionary. It would be always used without instantiation. I want to use it for a couple of things right after the app starts and then I want to release its memory. I thought ARC can take care of this.
You should not use autoreleased objects on static variables.
Change line...
elementToClassMapping = [NSMutableDictionary dictionaryWithObject:class forKey:element];
to
elementToClassMapping = [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObject:class] forKeys:[NSArray arrayWithObject:element]];
And also do not call your + (void)registerClass:(Class)class forElement:(NSString *)element from a 2nd thread without creating a autorelease pool.
Your class' +load is calling methods which calls autorelease.
Your class is loaded before main.
You can explicitly create an autorelease pool in +load:
+ (void)load
{
#autoreleasepool {
[self registerClass:[One class] forElement:#"one"];
[self registerClass:[Two class] forElement:#"two"];
}
}
However, it's often better to guarantee the order of your program's initialization and load explicitly before creating any threads in main:
int main(int argc, const char * argv[]) {
#autoreleasepool {
[DataFileRegistrar initializeStaticStuff];
...
what are you doing there is putting the dictionary on the stack (which is frankly stupid, because you have so many ways not to do that) and it leaks because it never gets deallocated from the stack.
NSDictionary is created to be used with autorelease pool (and yes..i know there are some cases where is better to use a static one but those cases are very very rare )
declare NSMutableDictionary *elementToClassMapping; in interface and everything will work just fine

NSMutableDictionary Singleton issue

I am coding Objective-C using the Cocos2D framework, and I have a singleton used for multiple purposes. One new purposes is to get and set character's "states" which are strings. I've recently made an NSDictionary for this purpose, but I have issues with the program freezing up when a method inside the singleton is called.
Here's the singleton code. I'm just leaving in the character state stuff:
.h
#interface ExGlobal : NSObject {
NSArray *charStates_keys;
NSArray *charStates_objects;
NSMutableDictionary *charStates;
}
#property(nonatomic, retain) NSMutableDictionary *charStates;
+(ExGlobal*)sharedSingleton;
- (NSString *)charState:(NSString *)charName;
- (void)set_charState:(NSString *)value forCharName:(NSString *)charName;
#end
.m
#import "ExGlobal.h"
#implementation ExGlobal
#synthesize charStates;
static ExGlobal* _sharedSingleton = nil;
+(ExGlobal*)sharedSingleton {
#synchronized([ExGlobal class]) {
if (!_sharedSingleton) {
[[self alloc] init];
}
return _sharedSingleton;
}
return nil;
}
+(id)alloc {
#synchronized([ExGlobal class]) {
NSAssert(_sharedSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedSingleton = [super alloc];
return _sharedSingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
exitName = #"ruinsSkyMid";
sceneChangeKind = #"reborn";
charStates = [[NSMutableDictionary alloc] init];
charStates_keys = [NSArray arrayWithObjects:#"Feathers", #"Hummus", nil];
charStates_objects = [NSArray arrayWithObjects:#"at wall", #"with Feathers", nil];
charStates = [NSMutableDictionary dictionaryWithObjects:charStates_objects forKeys:charStates_keys];
}
return self;
}
- (NSString *)charState:(NSString *)charName{
NSString *value = [charStates objectForKey:charName];
return value;
}
- (void)set_charState:(NSString *)charState forCharName:(NSString *)charName{
[charStates setObject:charState forKey:charName];
}
- (void)dealloc {
//I know it doesn't get called, but just in case
[charStates release];
[super dealloc];
}
#end
It's unclear to me what exactly the issue is when it freezes. When this happens, all I get in the console is:
Program received signal: “EXC_BAD_ACCESS”.
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.5 (8L1)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
Previous frame inner to this frame (gdb could not unwind past this frame)
Previous frame inner to this frame (gdb could not unwind past this frame)
Which I'm sure doesn't help finding the issue. I found if I redefine charStates_keys, charStates_objects and charStates inside both the charState and set_charState methods, it seems to work without freezing, except set_charState does not change the state.
It isn't freezing, it is crashing. Hence the EXC_BAD_ACCESS. It looks like your Xcode installation is borked, too, as the two messages following should not happen.
Note that methods should not have _s in the name; not a cause of the problem, but a comment on following convention.
You aren't retaining charStates and that is likely the cause of the crash.
Not an answer as such but I didn't have enough space in the comments field above to post this, but it might be useful.
As bbum already said, your lack of retaining charStates is likely the problem.
If you are confused about when to retain and not retain objects there's a really good book called "Learn Objective-C on the Mac" and I know it's a Mac book but most of it applies to iPhone too. On page 171 of chapter 9 (Memory Management) it talks about the "Memory Management Rules" and how if you are confused about when to retain or not then you don't understand the simple rules of Objective C memory management.
Essentially if you create an object using new, alloc or copy, then the retain count is automatically set to 1 so the object is retained and does not require you to retain it and will require a subsequent release to deallocate.
If you create the object any other way then the object will be an autoreleased object.
Obviously these rules only apply within the standard iOS libraries and can't necessarily be applied to third party libraries.
I recommend anyone who doesn't fully understand memory management in Objective C read this book. I found highly enlightening even for my iPhone work.
Hope that helps/.

Objective C Reassignment/Memory Management Crash

As a relative Objective-C beginner, I'm obviously still not grasping certain memory management rules. I can't figure out how to make this not crash:
#interface MyClass { NSArray *playerArray4th; }
- (void) viewDidLoad { playerArray4th = [self getAudioPlayersForSoundFile:#"rimshot" ofType:#"aif"]; }
- (NSArray*) getAudioPlayersForSoundFile:(NSString*)soundFileName ofType:(NSString*)soundFileType {
//code instantiating objects....
NSArray *toRet = [[NSArray alloc] initWithObjects:toRetTickPlayer,toRetTickPlayerCopy,toRetTickPlayerCopy2,toRetTickPlayerCopy3, nil];
return toRet;
}
Then later, in a different function:
NSArray *currentArray = playerArray4th;
[currentArray release];
currentArray = nil;
currentArray = [self getAudioPlayersForSoundFile:fileName ofType:ofType];
And it crashes when trying to access the array again:
- (void) playSound:(NSString*)soundType {
AVAudioPlayer *currentPlayer;
if ([soundType isEqualToString:#"4th"]) {
if (playerArray4thCounter >= [playerArray4th count]) playerArray4thCounter = 0;
NSLog(#"Playing from array: %#",playerArray4th);
currentPlayer = [playerArray4th objectAtIndex:playerArray4thCounter];
playerArray4thCounter++;
}
}
Try to learn about properties and about using getters and setters. Don't take shortcuts unless you know exactly what's going on.
So define the playerArray4th property in your header file:
#property (nonatomic,retain) NSArray *playerArray4th;
And then in your .m file create getter/setter:
#synthesize playerArray4th;
Then, always use self.playerArray4th for assigning and getting the variable. The prior objects will be released when needed.
So this will not leak:
self.playerArray4th = [NSArray arrayWithObjects:#"text",#"text",nil];
self.playerArray4th = [NSArray arrayWithObjects:#"new array",#"text",nil];
because the second assignment releases the first array.
Furthermore, read about using autorelease. In short, if you alloc, copy or new, you should either release or autorelease. There's a lot to read about this here on SO which I will not repeat here now.
Don't forget to put self.playerArray4th = nil; in your dealloc method.

Releasing With NSMutableArray?

I am allocating myMDD in main which contains an NSMutableArray instance variable (alloc/init-ed in init). When I add items to the NSMutableArray (frameList) I release after adding. The array and the objects it now contains are released at the bottom of main.
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MDD *myMDD = [[MDD alloc] init];
Frame *myFrame = [[Frame alloc] init];
[myMDD addFrame:myFrame];
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
[inFrame release];
}
// METHOD_ mdd dealloc
-(void)dealloc {
NSLog(#"_deal...: %#", self);
[frameList release];
[super dealloc];
}
My question is that the "static analyser" reports a potential memory leak, prefering to have the release for frame added main. (i.e)
int main (int argc, const char * argv[]) {
...
[myFrame release]; // Added
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
// [inFrame release];
}
I can see why this is, if I alloc myMDD and never call addFrame then I need to release it. Maybe its just a case of adding a autorelease to myMDD, but would that work in the situation where I call addFrame and the NSMutableArray is releasing the object?
EDIT_001
Changed to ...
int main (int argc, const char * argv[]) {
...
[myMDD addFrame:myFrame];
[myFrame release];
myFrame = nil;
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
}
gary
The reason you got that warning is because an NSMutableDArray retains any object put into it; likewise, when an NSMutableArray is released, it also releases any object contained within it. So let's look at your code.
This line:
Frame *myFrame = [[Frame alloc] init];
creates a new instance of Frame called myFrame. myFrame has a retain count of 1, because you used alloc/init to create it.
You then pass this to addFrame::
[myMDD addFrame:myFrame];
Which in turn puts it into an instance of an NSMutableArray:
[frameList addObject:inFrame];
At this point, inFrame and myFrame point to the same object. When added to the array, this object's retain count is incremented, so now it is 2.
Later on, back in main, you release myMDD, which releases frameList. Assuming frameList now has a retain count of 0, it is deallocated -- and, as an NSMutableArray, it releases any object it contains, which includes the object pointed to my myFrame.
So now myFrame's retain count is 1...so it doesn't get released, and you have a memory leak.
One Cocoa-y way to solve the problem is by autorelease myFrame:
Frame *myFrame = [[[Frame alloc] init] autorelease];
Which means it won't leak. Then, use the -[MDD dealloc] method in your second example (Edit_001). You're right that you shouldn't release inFrame in your addFrame method, since you're not retaining it.
As per convention, an add method should just retain the object if needed, not release it. And as a general rule, you should not release object that you did not retain, in your example the scope where you retained (created) the frame is not the same as in the addFrame method.
By scope I mean logic scope, not language scope.
In that particular example, you must call release just after addFrame. But the release should not be in the addFrame method.
In most cases, Cocoa provides class methods that initialize and return an autoreleased version of an object. i.e. [NSMutableDictionary dictionary] vs [[NSMutableDictionary alloc] init].
I advise to always use the class methods where possible if you're creating an object that you won't need to keep around or if you going to store it in a collection (NSArray, NSDictionary, NSSet, etc).
The general rule then is to only alloc objects your class owns directly (i.e. an instace or class variable, not inside a collection) and to use the class methods for all other cases.