I am trying to implement the countByEnumeratingWithState method in my objective-c class (say MyClass
In this method I do an
MyOtherClass *cl = [[MyOtherClass alloc] init];
buffer[count++] = cl;
The reason why I have to allocate objects on the fly is because those objects are stored 'elsewhere'.
However, when using this method from an application, it will crash:
for (const MyOtherClass *cl in myClassObj){
NSLog(#"obj: %#", cl.description);
}
The reason for this is most likely that ARC throws away my MyOtherClass object in countByEnumeratingWithState because the buffer is 'unretained'.
How can I make sure the MyOtherClass object 'retains' ?
More relevant information:
thread #4: tid = 0x5ca941, 0x0000000101a4cf8b libobjc.A.dylibobjc_msgSend + 11, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000101a4cf8b libobjc.A.dylibobjc_msgSend + 11
Why do you use const keyword? Const let you allocate object just for initialization and after then throw exception when you want to try change it. Try that:
for (MyOtherClass *cl in myClassObj){
NSLog(#"obj: %#", cl.description);
}
Related
I am facing a weird OC Exception, it looks that I am sending a message to a released address, but when I
try to check if it is NULL, it still crashs.
try to debug or add #try #catch, it catches nothing but runs for days, no crash at all.
exception
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000010
VM Region Info: 0x10 is not in any region. Bytes before following region: 4304617456
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
UNUSED SPACE AT START
--->
__TEXT 100934000-100a98000 [ 1424K] r-x/r-x SM=COW ...x/APP
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [18302]
Triggered by Thread: 22
code below is not strict ,just shows the logic ( code is running in one serial dispatch queue )
struct st_type {
void *arg;
};
static NSMutableDictionary *dictionary;
// init values
void init ()
{
// there is a static dictionary to retain the object
dictionary = [[NSMutableDictionary alloc]init];
// an object reference saved in dictionary and a struct holds it's pointer
NSObject *obj = [[NSObject alloc]init];
struct st_type *st = malloc(sizeof(struct st_type));
st->arg = (__bridge void *)obj;
dictionary[#"cached"] = obj;
// then the struct * passes to every where, I think it's safe because the object always in the dictionary.
...
}
// the only place to release the nsobject, so I think there is no chance to EXC_BAD_ACCESS, but ...
void release_object(struct st_type *st, NSObject obj){
[dictionary removeObjectForKey:#"cached"];
st->arg = NULL;
}
// some where to use the struct
void use_struct(struct st_type *st){
if(st->arg == NULL){
return;
}
// if I add try catch, it never crashs
// #try {
NSObject *obj = (__bridge NSObject*)st->arg;
[obj description]; // crash here.
// } #catch (NSException *exception) { print some log but it never reaches here... }
}
Could anyone help me what I can do next to fix this error?
If I understand correctly you want to store a reference to an Objective-C object as a void *. This is a similar use case as the old-style context or userInfo pointers that were passed as callbacks to sheets, for example.
See ARC: __bridge versus __bridge_retained using contextInfo test case for an example. I also assume you're using ARC (please read Casting and Object Lifetime Semantics).
Assuming the lifetime of the struct is longer than that of the Objective-C object, and that you explicitly set and release the object in the struct, you shouldn't need a dictionary for memory management.
Once the struct and object are allocated (using malloc and [[XX alloc] init] respectively), you can transfer ownership of the object out of ARC and store it in st->arg using a (__bridge_retained void *) cast.
To use the object, cast using (__bridge NSObject *). This will not change ownership.
When you are ready to release the object, pass ownership back to ARC by casting using (__bridge_transfer NSObject *). Then you can set the void * pointer to NULL.
So overall something like:
struct st_type {
void *arg;
};
void init()
{
NSObject *obj = [[NSObject alloc] init];
struct st_type *st = malloc(sizeof(struct st_type));
// transfer ownership out of ARC
st->arg = (__bridge_retained void *)obj;
}
void use_struct(struct st_type *st){
// no change in ownership
NSObject *obj = (__bridge NSObject *)st->arg;
// use `obj`
}
void release_object(struct st_type *st){
// transfer ownership back to ARC
NSObject *obj = (__bridge_transfer NSObject *)st->arg;
st->arg = NULL;
}
I am trying to get an Animation Helper with a cocos2d project and for some reason keep getting an error message:
unrecognized selector sent to class.
I have tried numerous approaches to no avail. I understand that it may have to do with a class-instance conflict, but I do not know how to resolve it. Any thoughts?
This is how I am calling the helper function:
CCAnimation* anim = [CCAnimation animationWithFrame:playerAnimName frameCount:1 delay:0.08f];
And this is the helper function itself:
+(CCAnimation*) animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay
{
printf("start helper");
// load the players's animation frames as textures and create a sprite frame
NSMutableArray* frames = [NSMutableArray arrayWithCapacity:frameCount];
for (int i = 1; i < frameCount+1; i++)
{
NSString* file = [NSString stringWithFormat:#"%#%i.png", frame, i];
CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:#"maze-art.plist"];
CCSpriteFrame* frame = [frameCache spriteFrameByName:file];
[frames addObject:frame];
}
// return an animation object from all the sprite animation frames
return [CCAnimation animationWithSpriteFrames:frames delay:delay];
}
Any insight is appreciated. Thanks!
In order for category to work, you must define the new method in the following way. Please refer to this http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
#interface ClassName ( CategoryName )
// method declarations
#end
For example, your new method must be defined as
#interface CCAnimation (Helper)
+(CCAnimation*) animationWithFile:(NSString*)name frameCount:(int)frameCount delay:(float)delay
{
...
}
#end
Where do your helper method is? If it is inside your own class, that you have to call it as
[MyClass method];
not
[CCAnimation method];
+ means that method is static so you must call it with the class inside which this method is.
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
I'm having an issue with memory management when dealing with callbacks and async code in objective c.
I cant seem to find a way to release the instance that the callback is set on.
For example:
MyClass *myArchive = [[MyClass alloc] init] ;
[myArchive callBack:^(RKObjectLoader* objectLoader, id object ) {
NSLog(#"success");
} fail:^(RKObjectLoader* objectLoader, NSError* error) {
NSLog(#"failed");
}];
[myArchive searchArchive:words:paging];
The problem being that I don't know when or how to release the instance *myArchive. Using Instruments within xcode to profile my code I always get a leak here. The function searchArchive performs an async request to a server using restkit. I wont reference the instance from within the callback as I heard this causes a retain cycle and I have done some reading about using __block and other c approaches to avoid retain cycles which is all fine but as it stands now with no actual code happening within the callback how do I release the *myArchive instance. anyone able to explain how I should deal with this within objective-c?
EDIT:
This is where I set the callback in myclass
// Sets internal backs on this object which basically wrap the delegate
//
- (void)callBack: (void (^)(RKObjectLoader* objectLoader, id object))success
fail: (void (^)(RKObjectLoader* objectLoader, NSError* error))fail {
//sanity check
NSAssert(_currentDelegate != self, #"Delegate is another object. Can not set callback");
// store our callback blocks in the instance
_success = [success copy] ;
_fail = [fail copy] ;
}
and then release _success and _fail in dealloc
and within the #interface
#interface myClass : NSObject<RKObjectLoaderDelegate> {
// holds the block callback for "success"
void (^_success)(RKObjectLoader* objectLoader, id object);
// holds the block callback for "fail"
void (^_fail)(RKObjectLoader* objectLoader, NSError* error);
}
I hope this gives more insight into what I'm doing wrong.
EDIT 2:
Ok I'm beginning to see the errors now:
-(void)retrieveGallery{
//create call back for async and deal with the result
[_galleryItems callBack:^(RKObjectLoader* objectLoader, NSArray *objects) {
//success happy days. do a bunch of code here that does not cause leaks
} fail:^(RKObjectLoader* objectLoader, NSError* error) {
//retry the attempt to retrieve gallery data from the server
_retryCount++;
if (_retryCount < _maxRetryCount) {
[self retrieveGallery];
}
}];
//read the collection of gallery items from server
[_galleryItems readGallery];
}
The only actual memory leaks are when the callback catches a fail for what ever reason and then calls the [self retrieveGallery] function from within callback to attempt again. this is what is causing the leak so I'm guessing that is a big no no. How should I attempt the function (retrieveGallery in this case) again.
Memory management isn't really any different because you are using an asynchronous callback. myArchive should be a property of whatever class you are doing this in. You want it to stick around until the task is complete, right?
#property (retain) MyClass *myArchive;
Then..
myArchive = [[MyClass alloc] init];
void (^on_success_callback)(void) = ^(void){
NSLog(#"success");
self.myArchive = nil;
};
You need to make sure you are managing the callbacks properly, i.e. copying them from the stack and releasing them when you are done.
If you have retains and releases in your code you probably aren't using the accessor methods properly.
I do not understand what I am doing wrong. I have a dictionary as a property of a singleton class:
#interface CABResourceManager : NSObject
{
....
NSMutableDictionary* soundMap;
}
#property (retain) NSMutableDictionary *soundMap;
Then I add an object to this dictionary in one class method:
+ (void)loadSoundFromInfo:(ABSoundInfo)sound
{
static unsigned int currentSoundID = 0;
CABSound* newSound = [[CABSound alloc] initWithInfo:(ABSoundInfo)sound soundID:++currentSoundID];
[[CABResourceManager sharedResMgr].soundMap setObject:newSound forKey:sound.name];
}
And try to get it in another method:
+ (ALuint)playSoundByName:(NSString*)name
{
NSMutableDictionary* map = [CABResourceManager sharedResMgr].soundMap;
CABSound *sound = [map objectForKey:name]; // here comes the exception
and the app exits on exception by that.
2011-03-27 20:46:53.943 Book3HD-EN[5485:207] *** -[NSCFSet objectForKey:]: unrecognized selector sent to instance 0x226950
2011-03-27 20:46:53.945 Book3HD-EN[5485:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException'
I guess it might have something with memory management, but hier it looks clear for me: CABSound object is retained in dictionary by doing setObject(), it should not be released at this time.
I'd check that soundMap is properly initialized. It looks like soundMap is a bad pointer at the time you get the error. It might happen to be nil in +loadSoundFromInfo, which wouldn't produce an error right away.
Make sure that you've initialized your soundMap in designated initializer:
// - (id) init... or something else
soundMap = [[NSMutableDictionary alloc] init];
Dont forget to override default dealloc implementation:
// class implementation file
- (void)dealloc {
[soundMap release];
//...release other objects you own...
[super dealloc];
}