I have an UIScrollView with drawing on it's content view. I also have zoom functionality. I am adding UIView with UITextViews as its subview on zoom and removing UIView with UITextViews when user pinch out zoom. Sometimes it works fine but sometimes its crashing.
Also console continuous shows different combinations of these and more :
CALayerGetSuperlayer called on instance of UIScrollViewDelayedTouchesBeganGestureRecognizer
CALayerGetSuperlayer called on instance of __NSCFDictionary
CALayerGetSuperlayer called on instance of __NSArrayM
CALayerGetSuperlayer called on instance of __NSCFString
CALayerGetSuperlayer called on instance of __NSCFType
I have attached a screenshot of the instrument with zombies template. Which shows the summary of message sent to deallocated object.
Some of my code which will useful for solving this problem.
I am returning CATiledLayer to ensure zoomed content is sharp.
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
}
return self;
}
+ layerClass
{
return [CATiledLayer class];
}
- (void)dealloc
{
CATiledLayer *tiledLayer = (CATiledLayer *)self.layer;
tiledLayer.contents=nil;
tiledLayer.delegate=nil;
[tiledLayer removeFromSuperlayer];
}
May be i'm wrong but, why do you instantiate a CATiledLayerin the dealloc method, this is physically wrong...
From what i know the reason of this error is either you are trying to dealloc an already deallocated object, or the object you are deallocating is empty
Related
Here's my class with my custom init method:
// Piece.h
#import <Foundation/Foundation.h>
#interface Piece : CCSprite
#property (nonatomic) int pieceNumber;
+(Piece *)initWithPieceImage:(UIImage*)piece pieceName:(int)pName;
#end
// Piece.m
#import "Piece.h"
#implementation Piece
#synthesize pieceNumber = _pieceNumber;
+(id)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [[[self alloc] initWithPieceImage:piece pieceName:pName] autorelease];
}
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
CCSprite *bgImage = nil;
if ( (self=[super init]) )
{
bgImage = [CCSprite spriteWithCGImage:piece.CGImage
key: [NSString stringWithFormat:#"%i",pName]];
}
return (Piece*)bgImage;
}
#end
I instantiated the Piece class like this to add it to the layer:
Piece *newPiece = [Piece initWithPieceImage:myUIImage pieceName:1];
[newPiece setPieceNumber:2]; //Error in this line
[self addChild: newPiece z:1];
However I have tried it like this and it perfectly works:
Piece *newPiece = [[Piece alloc] init];
[newPiece setPieceNumber:2];
but this is not what I want.
and here is the error I get:
[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Terminating app due to uncaught exception NSInvalidArgumentException, reason: -[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Aparently it looks like the problem is how Im trying to init my object.
I'm a newcomer to objective-c so I cant figure out what is wrong here.
any idea of what am I doing wrong here?
How can I achieve this approach and access the properties of my instantiated object with custom init method?
You have a mess in your code. In -(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName you return a CCSprite object instead of a Piece. You assign self with an object but return another, of an incorrect type.
init returns the correct type (because you haven't reimplemented it), so it works, but you haven't actually initialized the image correctly.
You need to change your method like so:
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [super initWithCGImage:piece.CGImage key:[NSString stringWithFormat:#"%i",pName]];
}
It is because in your init method, you are for some reason creating a CCSprite object and returning that instead of the Piece object. Because of that, it will not be an object of your Piece class and will not respond to any of Piece's methods.
Instead of creating a new CCSprite object to set those properties to, you want to set those on self or super.
I created a custom UITableViewCell (for this example, let's just say the subclass is MyViewCell) which has an Nib file associated to it MyViewCell.xib. The nib contains a UITableViewCell with one subview UIView (named cardContainer) that's simply a rectangle with a blue background. I want to add a drop shadow around the UIView, so I added set the layer properties in the -initWithCoder call:
#implementation MyViewCell
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initView];
}
return self;
}
- (void) initView
{
UIBezier Path*shadowPath =[UIBezierPath bezierPathWithRect:view.bounds];
[self.cardContainer.layer setShadowColor: [UIColor blackColor].CGColor];
[self.cardContainer.layer setShadowOpacity: 0.8];
[self.cardContainer.layer setShadowRadius:3.0];
[self.cardContainer.layer setShadowOffset: CGSizeMake(2.0,2.0)];
view.layer.shadowPath = shadowPath.CGPath;
}
#end
The problem I'm having is that these layer properties aren't being drawn. If I call the -initView call within awakeFromNib or drawRect it's drawn as expected. My question: why doesn't my original code work? Where should I be calling initView? Is there some view lifecycle? I understand that the initWithCoder doesn't have the outlets connected, but it didn't crash at runtime.
I read through Apple documentation around Views and searched through the SO questions without finding an answer. I found this SO answer, but again doesn't explain.
Hey I found a better way to do this ,just add some runtime attributes for your subview cardContainer
like this
no more code in .m file anymore.
EDIT:
From:NSNibAwaking Protocol
Important: Because the order in which objects are instantiated from an archive is not guaranteed, your initialization methods should not send messages to other objects in the hierarchy. Messages to other objects can be sent safely from within awakeFromNib—by which time it’s assured that all the objects are unarchived and initialized (though not necessarily awakened, of course).
You need to add this,
self.cardContainer.layer.masksToBounds = NO;
I have a strange issue with UIGestureRecognizer
I've create a class where i declare the gesture recognizer, and put self as a target
-(id)initWithTextView:(UITextView*)theTextView withDelegate:(id<WordSelectionDelegate>)theDelegate
{
if (self = [super init])
{
delegate = theDelegate;
textView = theTextView;
// init long press gesture to detect pressing on text elements
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressFromSender:)];
[textView addGestureRecognizer:longPressGesture];
}
return self;
}
But the trick is when i actually make a long press gesture i have next error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableAttributedString handleLongPressFromSender:]: unrecognized selector sent to instance 0x76227b0'
Why does the messages to self goes to String???
By the way, the problem is undoubtedly that the object that has the handleLongPressFromSender instance method (i.e. the object you're initializing with initWithTextView) is falling out of scope by the time the UILongPressGestureRecognizer is invoked. You need to check the the scope of that object.
For example, assuming that the name of this class was MyTextViewHandler, imagine you had a viewDidLoad for some view controller that had something like:
- (void)viewDidLoad
{
[super viewDidLoad];
// do a bunch of initialization
MyTextViewHandler *textViewDelegate = [[MyTextViewHandler alloc] initWithTextView:self.textview withDelegate:self];
}
If you did something like that in ARC project, you'd get the crash you describe (because the textViewDelegate object was a local object of viewDidLoad and will fall out of scope at the end of that method). If you make this delegate handler class an instance variable (or property) of your view controller, then this problem goes away.
I've created a custom class which is a subclass of CCLayer and trying to use it for CCScrollLayer
The way I do it is:
//Store the my layers to an NSMutableArray
for (AACustomClassLayer *cardLayer in levels) {
[layers addObject:cardLayer];
}
Under the hood of CCScrollLayer it crashes at:
- (void) updatePages
{
// Loop through the array and add the screens if needed.
int i = 0;
for (CCLayer *l in layers_)
{
l.anchorPoint = ccp(0,0);
l.contentSize = [CCDirector sharedDirector].winSize;
l.position = ccp( (i * (self.contentSize.width - self.pagesWidthOffset)), 0 );
if (!l.parent)
[self addChild:l];
i++;
}
}
The implementation for the AACustomClassLayer class (subclass of CCLayer) looks like:
-(id)initWithChapter:(AALevel *)level {
self = [super init];
if (self) {
self.isTouchEnabled = YES;
//Here I'm adding the CCSprite to my layer
}
return self;
}
UPDATE:
Crash log
2012-04-20 14:12:12.344 [15780:10a03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary setAnchorPoint:]: unrecognized selector sent to instance 0x884ab40'
*** First throw call stack:
(0x1a75022 0x200fcd6 0x1a76cbd 0x19dbed0 0x19dbcb2 0xd013f 0xcfe2b 0x102370 0x44c15 0xbe45f 0x8a94be 0x8aa274 0x8b9183 0x8b9c38 0x8ad634 0x282def5 0x1a49195 0x19adff2 0x19ac8da 0x19abd84 0x19abc9b 0x8a9c65 0x8ab626 0xbda06 0x22e5)
terminate called throwing an exception
I've found it!
for (AACustomClassLayer *cardLayer in levels) {
cardLayer = [[AACustomClassLayer node] autorelease];
[layers addObject:cardLayer];
}
You should add a conditional check in your for loop that determines if the object you are getting from enumeration is in fact a CCLayer. Your crash log states that the anchorPoint setter was not available on some object, presumably on an object in your layers_ array since that is the code you have posted that deals with anchorPoints.
Enumeration is convenient, but you are casing all objects to CCLayer when it is possible that one of them is not. I don't know where you are adding objects to layers_ but is it possible you are adding an object that is not actually a CCLayer ?
Im using a Base view controller for some other view controllers,
Im making it the base as I have 4 to 6 of other view controllers that will be showing the same label and image...
so I tried to make this base page and subClass the other ViewControllers,
my doubt is that if I leave the dealloc for the strings that contain the value for the label and other string, when dealloc is called for that page, then I get an exception
pointer being freed was not allocated
but I dont want just to comment out the deallocs for the labels,
so What im i doing wrong, missing?
here the BasePage
#import "BasePage.h"
#implementation BasePage
#synthesize titleString = _titleString;
#synthesize justifyTitleString = _justifyTitleString;
- (void) dealloc {
[_titleString dealloc];
[_justifyTitleString dealloc];
[super dealloc];
}
- (id)initWithParams:(NSString *)title :(NSString *)justifyTitle
{
self = [super init];
if (self) {
self.titleString = title;
self.justifyTitleString = justifyTitle;
}
return self;
}
my app uses a navigation controller,
so when I call a page i use:
CentresVC *centresVC = [[[CentresVC alloc]initWithParams:[NSString stringWithFormat:#"Centre %d", indexPath.row]:#"center"]autorelease];
[self.navigationController pushViewController:centresVC animated:YES];
and pop the views when navigating back,
So I have the doubt of when using
[_titleString dealloc];
[_justifyTitleString dealloc];
where the pointer for those labels get saved?, and if i just commented out wich doesnt seem nice, would i get then a memory leak that would crash?
how to fix this?
thanks!
As Richard said, you shouldn't be calling other objects' dealloc method. Ever.
If titleString and justifyTitleString are retained/strong properties, then this version below would work. Assuming that no other objects had retained titleString and justifyTitleString, their retain counts would go to 0, and their dealloc methods would be called automatically.
- (void) dealloc {
[_titleString release];
[_justifyTitleString release];
[super dealloc];
}
This next option would work too, since the synthesized setter for a strong/retained property sends release to the old value of the property before assigning the new one. Also, this would be preferred if you had overridden your setter to do additional cleanup of the old value.
- (void) dealloc {
self.titleString = nil;
self.justifyTitleString = nil;
[super dealloc];
}
Finally, if you were using ARC, and you didn't need additional cleanup like in the second case above, you wouldn't need to override dealloc at all. But if you did need to override dealloc, you would omit [super dealloc] since that would be provided automatically by the compiler.