I have a scrollview, which is a subview of my main view. When shown this scrollview displays 8 subviews, 3 of which are visible on the screen at any one time. The user can scroll left/right to view them, and tap to make a selection.
Once a selection is made, I look to see if the selected object is the same as the current selection, and if not, release the current one and retain the new one. If they're identical then I don't do anything.
When I create the 8 subviews for my scrollview, I create them one-at-a-time, ad to the subview, then release. I figure since the view retains a copy, I don't need a copy. Now I'm suspecting this might be my problem, but I really don't see why since everything seems to be working (well sort-of). Anyway...
I need to point out that the 8 subviews are subclassed UIImageViews. This is so each can alter its own alpha to appear faded slightly as it moves past the center of the screen. The object that's in the center appears will change its alpha to 100% opaque.
Here's the relevant code...
interface for the scrollview:
@interface RootViewController : UIViewController <UIScrollViewDelegate, thumbViewDelegate> {
id<RootViewControllerDelegate> _delegate
PagingScrollView *thumbScrollView;
ChairPart *selectedChairPart;
}
@property (nonatomic, retain) PagingScrollView *thumbScrollView;
@property (nonatomic, retain) ChairPart *selectedChairPart;
implementation for the scrollview (selectedChairPart initialized in viewDidLoad):
@synthesize selectedChairPart;
@synthesize thumbScrollView;
- (void)createThumbScrollViewIfNecessary {
if (![self thumbScrollView]) {
self.m_pageControl.currentPage = 1;
float scrollViewHeight = THUMB_HEIGHT;
float scrollViewWidth = THUMB_WIDTH;
self.thumbScrollView = [[PagingScrollView alloc] initWithFrame:CGRectMake(234, 5, scrollViewWidth, scrollViewHeight)];
[[self thumbScrollView] setCanCancelContentTouches:NO];
[[self thumbScrollView] setClipsToBounds:NO];
[[self thumbScrollView] setDelegate:self];
[[self thumbScrollView] setShowsHorizontalScrollIndicator:NO];
[[self thumbScrollView] setShowsVerticalScrollIndicator:NO];
[[self thumbScrollView] setPagingEnabled:YES];
// now place all the thumb views as subviews of the scroll view
// and in the course of doing so calculate the content width
float xPosition = 0;
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:0];
int count = [sectionInfo numberOfObjects];
NSIndexPath *indexPath = nil;
for (int i = 0; i < count; i++) {
indexPath = [NSIndexPath indexPathForRow: i inSection: 0];
ChairPart *part = [fetchedResultsController objectAtIndexPath:indexPath ];
UIImage *thumbImage = [UIImage imageNamed:[NSString stringWithFormat:@"%@", [part icon]]];
if (thumbImage) {
ThumbView *thumbView = [[ThumbView alloc] initWithImage:thumbImage];
[thumbView setDelegate:self];
[thumbView setChairPart:part];
[thumbView setAlpha:0.50f];
if (i == 0) {
[thumbView setAlpha:1.0];
}
CGRect frame = [thumbView frame];
frame.origin.y = THUMB_V_PADDING;
frame.origin.x = xPosition;
frame.size.width += 100;
[thumbView setFrame:frame];
[thumbView setTag:555 + i];
xPosition += frame.size.width;
[[self thumbScrollView] addObserver:thumbView
forKeyPath:@"contentOffset"
options:NSKeyValueObservingOptionNew
context:@selector(updateAlpha:)];
[[self thumbScrollView] addSubview:thumbView];
[thumbView release];
}
}
[[self thumbScrollView] setContentSize:CGSizeMake(xPosition, scrollViewHeight)];
}
}
implementation for tapped/selected image:
- (void)thumbViewWasTapped:(ThumbView *)tiv {
NSLog(@"------------thumbview Tapped-----------------------");
NSLog(@"from:%p to:%p",[self selectedChairPart], [tiv chairPart]);
if ([self selectedChairPart] != [tiv chairPart]) {
if ([self selectedChairPart] != nil) {
[selectedChairPart release];
} else {
NSLog(@"selectedChairPart is NIL!!!!!!!!!!");
}
if (tiv == nil) {
NSLog(@"------TIV = NILLLLL");
}
NSLog(@"tiv is:%@", tiv);
// crash happens on the next line...
[self setSelectedChairPart:[tiv chairPart]];
} else {
NSLog(@"chairparts identical");
}
[self toggleThumbView];
}
interface for the thumbview:
@class ChairPart;
@protocol ThumbViewDelegate;
@interface ThumbView : UIImageView {
id <ThumbViewDelegate> delegate;
ChairPart *chairPart;
UIImageView *iconView;
CGPoint touchLocation; // Location of touch in own coordinates (stays constant during dragging).
}
@property (nonatomic, assign) id <ThumbViewDelegate> delegate;
@property (nonatomic, retain) UIImageView *iconView;
@property (nonatomic, retain) ChairPart *chairPart;
@property (nonatomic, assign) CGPoint touchLocation;
@end
@protocol ThumbViewDelegate <NSObject>
@optional
- (void)thumbViewWasTapped:(ThumbView *)tiv;
- (void)thumbViewStartedTracking:(ThumbView *)tiv;
- (void)thumbViewStoppedTracking:(ThumbView *)tiv;
@end
implementation for the thumbviews:
float distanceBetweenPoints(CGPoint a, CGPoint b);
@implementation ThumbView
@synthesize delegate;
@synthesize touchLocation;
@synthesize chairPart;
@synthesize iconView;
- (id)initWithImage:(UIImage *)image {
CGSize size = [image size];
CGRect rect = CGRectMake(0, 0, size.width, size.height);
self = [super initWithFrame:rect];
UIImageView *myImageView = [[UIImageView alloc] initWithImage:image];
self.iconView = myImageView;
CGRect frame = [myImageView frame];
frame.origin.x += 50;
[self.iconView setFrame:frame];
[self addSubview:self.iconView];
[myImageView release];
if (self) {
[self setUserInteractionEnabled:YES];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// store the location of the starting touch so开发者_如何学Go we can decide when we've moved far enough to drag
touchLocation = [[touches anyObject] locationInView:self];
if ([delegate respondsToSelector:@selector(thumbViewStartedTracking:)])
[delegate thumbViewStartedTracking:self];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([[touches anyObject] tapCount] == 1) {
CGFloat alpha = [self alpha];
if (alpha > 0.31) {
if ([delegate respondsToSelector:@selector(thumbViewWasTapped:)])
[delegate thumbViewWasTapped:self];
}
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
if ([delegate respondsToSelector:@selector(thumbViewStoppedTracking:)])
[delegate thumbViewStoppedTracking:self];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([object isKindOfClass:[PagingScrollView class]]) {
PagingScrollView *parentView = (PagingScrollView *)object;
if ( (parentView != nil) && ([parentView alive]) ) {
[self performSelector:(SEL)context withObject:change];
} else {
[parentView removeObserver:self forKeyPath:@"contentOffset"];
}
}
}
- (void)updateAlpha:(NSDictionary *)change {
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CGFloat offset = [[change objectForKey:NSKeyValueChangeNewKey] CGPointValue].x;
CGFloat origin = [self frame].origin.x;
CGFloat delta = fabs(origin - offset);
CGFloat mod = 1 - delta/self.frame.size.width;
[UIView beginAnimations:@"Fading" context:nil];
if (delta < [self frame].size.width) {
self.alpha = mod * 0.7 + .2;
[self _iconView].transform = CGAffineTransformMakeScale(mod * 1.0 + 0.5, mod * 1.0 + 0.5);
} else {
mod = delta/self.frame.size.width;
[self _iconView].transform = CGAffineTransformMakeScale(mod * 1.0 - 0.5, mod * 1.0 - 0.5);
self.alpha = 0.3;
}
[UIView commitAnimations];
// [pool drain];
}
- (void)dealloc {
NSLog(@"ThumView dealloc");
[iconView release];
[chairPart release];
[super dealloc];
}
@end
float distanceBetweenPoints(CGPoint a, CGPoint b) {
float deltaX = a.x - b.x;
float deltaY = a.y - b.y;
return sqrtf( (deltaX * deltaX) + (deltaY * deltaY) );
}
I didn't think it was necessary to store the thumbviews in an array as the scrollview retains a copy of each, and the selected thumbview retains a copy as well. I set a bunch of nslog statements, checking the location of the line that causes the crash as well as showing the contents of the variables/objects at different points in the execution.
I thought maybe I'm just doing something wrong and hopefully someone here can point it out. I wasn't able to find anything through build & analyze or leak performance.
Thanks in advance for your help.
here's the console output:
2011-04-25 07:07:02.444 iRocker[45162:207] ------------thumbview Tapped-----------------------
2011-04-25 07:07:02.445 iRocker[45162:207] from:0x0 to:0x5a62bd0
2011-04-25 07:07:02.446 iRocker[45162:207] selectedChairPart is NIL!!!!!!!!!!
2011-04-25 07:07:02.448 iRocker[45162:207] tiv is:<ThumbView: 0x5a83cd0; baseClass = UIImageView; frame = (0 10; 301 351); alpha = 0.9; tag = 555; layer = <CALayer: 0x5a83ed0>>
2011-04-25 07:07:15.601 iRocker[45162:207] ------------thumbview Tapped-----------------------
2011-04-25 07:07:15.602 iRocker[45162:207] from:0x5a62bd0 to:0x5a62f20
2011-04-25 07:07:15.603 iRocker[45162:207] tiv is:<ThumbView: 0x5a85ef0; baseClass = UIImageView; frame = (301 10; 301 351); alpha = 0.9; tag = 556; layer = <CALayer: 0x5a867d0>>
2011-04-25 07:07:23.618 iRocker[45162:207] ------------thumbview Tapped-----------------------
2011-04-25 07:07:23.619 iRocker[45162:207] from:0x5a62f20 to:0x5a62bd0
2011-04-25 07:07:23.620 iRocker[45162:207] tiv is:<ThumbView: 0x5a83cd0; baseClass = UIImageView; frame = (0 10; 301 351); alpha = 0.9; tag = 555; layer = <CALayer: 0x5a83ed0>>
2011-04-25 07:07:23.620 iRocker[45162:207] *** -[NSConcreteNotification retain]: message sent to deallocated instance 0x5a62bd0
(gdb) bt
#0 0x013bd057 in ___forwarding___ ()
#1 0x013bcf22 in __forwarding_prep_0___ ()
#2 0x015ab7f6 in objc_setProperty ()
#3 0x00007ad9 in -[RootViewController setSelectedChairPart:] (self=0x5a1b4c0, _cmd=0x4375d, _value=0x5a62bd0) at /Users/smorrison/Desktop/Val sample code/ChairBuilder2/Classes/RootViewController.mm:77
#4 0x0000905d in -[RootViewController thumbViewWasTapped:] (self=0x5a1b4c0, _cmd=0x439c4, tiv=0x5a83cd0) at /Users/smorrison/Desktop/Val sample code/ChairBuilder2/Classes/RootViewController.mm:1088
#5 0x000142f9 in -[ThumbView touchesEnded:withEvent:] (self=0x5a83cd0, _cmd=0x77c0e2, touches=0xfa4c080, event=0xfa4bfc0) at /Users/smorrison/Desktop/Val sample code/ChairBuilder2/Classes/ThumbView.m:61
#6 0x005f2987 in _UIGestureRecognizerSortAndSendDelayedTouches ()
#7 0x005f30fc in _UIGestureRecognizerUpdateObserver ()
#8 0x0142cfbb in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#9 0x013c20e7 in __CFRunLoopDoObservers ()
#10 0x0138abd7 in __CFRunLoopRun ()
#11 0x0138a240 in CFRunLoopRunSpecific ()
#12 0x0138a161 in CFRunLoopRunInMode ()
#13 0x01d80268 in GSEventRunModal ()
#14 0x01d8032d in GSEventRun ()
#15 0x0037642e in UIApplicationMain ()
#16 0x00002ae4 in main (argc=1, argv=0xbfffef7c) at /Users/smorrison/Desktop/Val sample code/ChairBuilder2/main.m:14
The solution was to remove the line:
[selectedChairPart release];
from the ThumbViewWasTapped method. I thought this would cause a leak as the property uses retain. It doesn't cause a leak and the crash is gone.
I would really like to understand how this is solved though, it just doesn't make sense. Any comments would be greatly appreciated.
精彩评论