0

我是 iPhone 编程的另一个菜鸟,我有一个非常基本的问题,我无法回答。

首先,我在 xcode 3.1.4 中编写代码,以便我可以在旧平台上学习基础知识,并希望在不久的将来这将使我能够创建便携式应用程序(从某种意义上说,我将支持 iPhone 3以及)。不幸的是,据我了解,苹果已经剥离了 xcode 3.1.4 附带的文档,这使得很难猜测什么是正确的做事方式,因为当前的文档和示例建议的路由与 iOS 5 更相关(例如故事板)。因此,即使我似乎理解了 MVC 的整个概念,并且虽然我的第一个真正的玩具应用程序实际上正在运行,但它肯定没有正确管理内存,因为没有使用相关的 dealloc 方法(我拥有的相关 NSLogs 没有显示)。

现在具体说。我想创建一个单窗口应用程序(多边形,作业 3)。让我们忘记界面构建器;所有视图/子视图都将以编程方式创建。为此,我创建了一个自定义 UIViewController。我还创建了一个自定义 UIView 来描述我想要呈现的单个视图的自定义子视图。(很可能我们已经不同意我在这里的方法,但现在我真的相信我在这里尝试的东西在概念上是正确的)。在应用程序委托 (.h) 中,我声明了我的 mainController,这就是我真正声明的全部内容。该文件如下:

#import <UIKit/UIKit.h>
#import "MainViewController.h"

@interface MySimpleHelloPolyAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MainViewController * mainController;
}

@property (nonatomic, retain) IBOutlet UIWindow * window;
@property (nonatomic, assign) IBOutlet MainViewController * mainController;

@end

在运行时,我希望 mainController 为呈现给用户的主视图分配必要的内存,并添加我的自定义 UIView 的子视图(这将是绘制多边形的画布)。因此,本质上我想将窗口视为一个容器,mainController 的视图实际上向用户呈现主视图,并且我还将附加一个带有我们自定义画布实例(自定义视图)的子视图。下面是 AppDelegate.m 文件:

#import "MySimpleHelloPolyAppDelegate.h"

@implementation MySimpleHelloPolyAppDelegate

@synthesize window, mainController;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    [window setBackgroundColor:[UIColor blueColor]]; // I do not want to see 'blue' on runtime

    // On the creation of the MainViewController subclass I indicated that I want a nib for my new controller
    // simply because this is the recommended way by apple. Apparently this should be an overkill for a simple
    // one-window app, but nevertheless it should be ok.
    mainController = [[MainViewController alloc] initWithNibName:nil bundle:nil];
    [window addSubview:mainController.view];

    [window makeKeyAndVisible];
}

- (void)dealloc {
    NSLog(@"(AppDelegate): Dealloc is called");
    [mainController.view removeFromSuperview];
    [mainController release];
    [window release];
    [super dealloc];
}

@end

让我们看看 MainViewController.h:

#import <UIKit/UIKit.h>
#import "Polygon.h"
#import "Canvas.h"

@interface MainViewController : UIViewController {
    IBOutlet UILabel * numSidesTextLabel;
    IBOutlet UILabel * numSidesValueLabel;
    IBOutlet UILabel * advancedOptionsLabel;
    IBOutlet UILabel * polygonNameLabel;
    IBOutlet UISwitch * advancedOptionsSwitch; // Not supported yet
    IBOutlet UIButton * increaseButton;
    IBOutlet UIButton * decreaseButton;
    IBOutlet Canvas * myCanvas;
    Polygon * myPolygon;
}

@property (nonatomic, retain) IBOutlet UILabel * numSidesTextLabel;
@property (nonatomic, retain) IBOutlet UILabel * numSidesValueLabel;
@property (nonatomic, retain) IBOutlet UIButton * increaseButton;
@property (nonatomic, retain) IBOutlet UIButton * decreaseButton;
@property (nonatomic, retain) IBOutlet UIView * myCanvas;
@property (nonatomic, retain) IBOutlet Polygon * myPolygon;

- (IBAction) increase;
- (IBAction) decrease;
- (void) updateInterface;

@end

现在让我们看看 MainViewController.m:

#import "MainViewController.h"

@implementation MainViewController

@synthesize numSidesTextLabel, numSidesValueLabel, decreaseButton, increaseButton, myCanvas, myPolygon;

- (IBAction) decrease {
    [myPolygon setNumberOfSides:([myPolygon numberOfSides]-1)];
    [self updateInterface];
}

- (IBAction) increase {
    [myPolygon setNumberOfSides:([myPolygon numberOfSides]+1)];
    [self updateInterface];
}

- (void) updateInterface {
    int sides = [myPolygon numberOfSides];

    UIColor * myOceanColor = [UIColor colorWithRed:0.00 green:0.333 blue:0.557 alpha:1.00];
    [increaseButton setTitleColor:myOceanColor forState:UIControlStateNormal];
    [increaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
    [decreaseButton setTitleColor:myOceanColor forState:UIControlStateNormal];
    [decreaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];

    numSidesValueLabel.text = [NSString stringWithFormat:@"%d", sides];
    decreaseButton.enabled = YES; increaseButton.enabled = YES;

    if (sides == MAX_NUM_SIDES_CONSTANT) increaseButton.enabled = NO;
    else if (sides == MIN_NUM_SIDES_CONSTANT) decreaseButton.enabled = NO;

    [myCanvas updateState:sides withName:[myPolygon name]];
    [myCanvas setNeedsDisplay];
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
    }
    return self;
}

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:CGRectMake(0.0, 20.0, 320.0, 460.0)];
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)viewDidLoad {
    CGRect tempFrame;
    UIColor * oceanColor = [UIColor colorWithRed:0.000 green:0.333 blue:0.557 alpha:1.000];

    // Draw the labels
    tempFrame = CGRectMake(20.0, 20.0, 150.0, 20.0);
    numSidesTextLabel = [[UILabel alloc] initWithFrame:tempFrame];
    numSidesTextLabel.text = @"Number of sides:";
    numSidesTextLabel.textAlignment = UITextAlignmentLeft;
    [self.view addSubview:numSidesTextLabel];
    [numSidesTextLabel release];

    tempFrame = CGRectMake(160.0, 20.0, 40.0, 20.0);
    numSidesValueLabel = [[UILabel alloc] initWithFrame:tempFrame];
    numSidesValueLabel.text = @"-----";
    numSidesValueLabel.textAlignment = UITextAlignmentLeft;
    [self.view addSubview:numSidesValueLabel];
    [numSidesValueLabel release];

    tempFrame = CGRectMake(20.0, 125.0, 160.0, 20.0);
    advancedOptionsLabel = [[UILabel alloc] initWithFrame:tempFrame];
    advancedOptionsLabel.text = @"Advanced options";
    advancedOptionsLabel.textAlignment = UITextAlignmentLeft;
    [self.view addSubview:advancedOptionsLabel];
    [advancedOptionsLabel release];

    // Draw the advanced switch
    tempFrame = CGRectMake(205.0, 120.0, 120.0, 60.0);
    advancedOptionsSwitch = [[UISwitch alloc] initWithFrame:tempFrame];
    [advancedOptionsSwitch setOn:NO animated:YES];
    [self.view addSubview:advancedOptionsSwitch];
    [advancedOptionsSwitch release];

    // Decrease Button
    decreaseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];    // autoreleased
    [decreaseButton setTitle:@"Decrease" forState:UIControlStateNormal];
    [decreaseButton setTitleColor:oceanColor forState:UIControlStateNormal];
    [decreaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
    decreaseButton.frame = CGRectMake(20.0, 60.0, 110.0, 40.0);
    [decreaseButton addTarget:self action:@selector(decrease) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:decreaseButton];

    // Increase Button
    increaseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];    // autoreleased
    [increaseButton setTitle:@"Increase" forState:UIControlStateNormal];
    [increaseButton setTitleColor:oceanColor forState:UIControlStateNormal];
    [increaseButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateDisabled];
    increaseButton.frame = CGRectMake(190.0, 60.0, 110.0, 40.0);
    [increaseButton addTarget:self action:@selector(increase) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:increaseButton];

    // Initialize the polygon
    myPolygon = [[Polygon alloc] init];
    numSidesValueLabel.text = [NSString stringWithFormat:@"%d", [myPolygon numberOfSides]];

    // Prepare the canvas
    CGRect myCanvasFrame = CGRectMake(20.0, 160.0, 280.0, 280.0);
    myCanvas = [[Canvas alloc] initWithFrame:myCanvasFrame withNumSides:[myPolygon numberOfSides] withPolygonName:[myPolygon name]];
    [self.view addSubview:myCanvas];
    [myCanvas release];

    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    numSidesTextLabel = nil;
    numSidesValueLabel = nil;
    advancedOptionsLabel = nil;
    myPolygon = nil;
    myCanvas = nil;
    [self.view release]; self.view = nil;
}

- (void)dealloc {
    NSLog(@"(MainViewController): Dealloc is called");
    [numSidesTextLabel removeFromSuperview];
    [numSidesValueLabel removeFromSuperview];
    [advancedOptionsLabel removeFromSuperview];
    [myPolygon release];
    [myCanvas removeFromSuperview];     // This should generate additional output
    [super dealloc];
}

@end

现在让我们看一下 Canvas.h 文件:

#import <UIKit/UIKit.h>
#import "PrepDefs.h"

@interface Canvas : UIView {
    IBOutlet UILabel * polygonNameLabel;
    int currentStateNumSides;
    CGPoint center;
}

@property (nonatomic, retain) UILabel * polygonNameLabel;
@property (nonatomic, assign) int currentStateNumSides;
@property (nonatomic, assign) CGPoint center;

+ (NSArray *) pointsForPolygonInRect:(CGRect) rect numberOfSides:(int) numberOfSides;

- (id) initWithFrame:(CGRect)frame withNumSides:(int)sides withPolygonName:(NSString *) name;
- (void) updateState:(int)sides withName:(NSString *)name;

@end

Canvas.m 文件:

#import "Canvas.h"

@implementation Canvas

@synthesize polygonNameLabel, currentStateNumSides, center;

+ (NSArray *) pointsForPolygonInRect:(CGRect)rect numberOfSides:(int)numberOfSides { 
    CGPoint center = CGPointMake(rect.size.width / 2.0, rect.size.height / 2.0); 
    float radius = 0.9 * center.x; 
    NSMutableArray *result = [NSMutableArray array]; 
    float angle = (2.0 * M_PI) / numberOfSides; 
    float exteriorAngle = M_PI - angle; 
    float rotationDelta = angle - (0.5 * exteriorAngle); 

    for (int currentAngle = 0; currentAngle < numberOfSides; currentAngle++) { 
            float newAngle = (angle * currentAngle) - rotationDelta; 
            float curX = cos(newAngle) * radius; 
            float curY = sin(newAngle) * radius; 
            [result addObject:[NSValue valueWithCGPoint:CGPointMake(center.x + curX, center.y + curY)]]; 
    } 

    return result; 
} 

- (id) initWithFrame:(CGRect)frame withNumSides:(int)sides withPolygonName:(NSString *)name {
    if (self = [super initWithFrame:frame]) {
            // Initialization code
            [self setBackgroundColor:[UIColor brownColor]];
            currentStateNumSides = sides;
            center = CGPointMake ([self bounds].size.width / 2.0, [self bounds].size.height / 2.0);

            polygonNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, center.y - 10.0, [self bounds].size.width, 20.0)];
            polygonNameLabel.text = name;
            polygonNameLabel.textAlignment = UITextAlignmentCenter;
            polygonNameLabel.backgroundColor = [UIColor clearColor];
            [self addSubview:polygonNameLabel];
            [polygonNameLabel release];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame {
    NSLog(@"(Canvas, -initWithFrame): You should always use initWithFrame:withNumSides:withPolygonName:");
    return [self initWithFrame:frame withNumSides:DEFAULT_NUM_SIDES withPolygonName:DEFAULT_POLYGON_NAME];
}

- (void)drawRect:(CGRect)rect {
    // Drawing code
    NSArray * points = [Canvas pointsForPolygonInRect:[self bounds] numberOfSides:[self currentStateNumSides]];

    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    [[UIColor blackColor] set];
    UIRectFrame ([self bounds]);

    CGContextBeginPath (currentContext);
    for (int i = 0; i < [points count]; i++) {
            CGPoint currentPoint;
            NSValue * currentValue;
            currentValue = [points objectAtIndex:i];
            currentPoint = [currentValue CGPointValue];
            //NSLog(@"(%.1f, %.1f)", currentPoint.x, currentPoint.y);
            if (i == 0)
                    CGContextMoveToPoint(currentContext, currentPoint.x, currentPoint.y);
            else
                    CGContextAddLineToPoint(currentContext, currentPoint.x, currentPoint.y);
    }
    CGContextClosePath(currentContext);

    [[UIColor yellowColor] setFill];
    [[UIColor blackColor] setStroke];

    CGContextDrawPath(currentContext, kCGPathFillStroke);
}

- (void) updateState:(int)sides withName:(NSString *)name {
    currentStateNumSides = sides;
    polygonNameLabel.text = name;
}

- (void)dealloc {
    NSLog(@"(Canvas): Calling dealloc");
    [polygonNameLabel removeFromSuperview]; polygonNameLabel = nil;
    [super dealloc];
}

@end

PrepDefs.h 文件:

#ifndef __PREP_DEFS_H__
#define __PREP_DEFS_H__

#define MIN_NUM_SIDES_CONSTANT 3
#define DEFAULT_NUM_SIDES 5
#define MAX_NUM_SIDES_CONSTANT 12

#define DEFAULT_POLYGON_NAME @"Pentagon"

#endif

最后是 Polygon 类的文件。Polygon.h 文件:

#import <Foundation/Foundation.h>
#import "PrepDefs.h"

@interface Polygon : NSObject {
    int numberOfSides;
    int minimumNumberOfSides;
    int maximumNumberOfSides;
}

@property int numberOfSides;
@property int minimumNumberOfSides;
@property int maximumNumberOfSides;
@property (readonly) float angleInDegrees;
@property (readonly) float angleInRadians;
@property (readonly) NSString * name;

- (void) setNumberOfSides:(int)numSides;
- (void) setMinimumNumberOfSides:(int)minNumSides;
- (void) setMaximumNumberOfSides:(int)maxNumSides;

- (id) initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max;

@end

Polygon.m 文件:

#import "Polygon.h"

@implementation Polygon

@synthesize numberOfSides, minimumNumberOfSides, maximumNumberOfSides, angleInDegrees, angleInRadians, name;

- (void) setNumberOfSides:(int)numSides {
    if ((numSides >= [self minimumNumberOfSides]) && (numSides <= [self maximumNumberOfSides])) numberOfSides = numSides;
    else NSLog(@"PolygonShape [setNumberOfSides]: Assignment out of bounds");
}

- (void) setMinimumNumberOfSides:(int)minNumSides {
    if (minNumSides >= MIN_NUM_SIDES_CONSTANT) minimumNumberOfSides = minNumSides;
    else NSLog(@"PolygonShape [setMinimumNumberOfSides]: Assignment out of bounds");
}

- (void) setMaximumNumberOfSides:(int)maxNumSides {
    if (maxNumSides <= MAX_NUM_SIDES_CONSTANT) maximumNumberOfSides = maxNumSides;
    else NSLog(@"PolygonShape [setMaximumNumberOfSides]: Assignment out of bounds");
}

- (float) angleInDegrees {
    int sides = [self numberOfSides];
    return ((float) (180.0 * ((double) (sides - 2)) / ((double) sides)));
}

- (float) angleInRadians {
    int sides = [self numberOfSides];
    return ((double) M_PI) * ((double) (sides - 2.0)) / ((double) sides);
}

- (NSString *) name {
    NSString * s;

    switch ([self numberOfSides]) {
            case 3: s = [NSString stringWithString:@"Triangle"]; break;
            case 4: s = [NSString stringWithString:@"Square"]; break;
            case 5: s = [NSString stringWithString:@"Pentagon"]; break;
            case 6: s = [NSString stringWithString:@"Hexagon"]; break;
            case 7: s = [NSString stringWithString:@"Heptagon"]; break;
            case 8: s = [NSString stringWithString:@"Octagon"]; break;
            case 9: s = [NSString stringWithString:@"Enneagon"]; break;
            case 10: s = [NSString stringWithString:@"Decagon"]; break;
            case 11: s = [NSString stringWithString:@"Hendecagon"]; break;
            case 12: s = [NSString stringWithString:@"Dodecagon"]; break;
            default: NSLog(@"PolygonShape [name]: I should never enter here."); s = nil; break;
    }

    return s;
}

- (id) init {
    return [self initWithNumberOfSides:DEFAULT_NUM_SIDES minimumNumberOfSides:MIN_NUM_SIDES_CONSTANT maximumNumberOfSides:MAX_NUM_SIDES_CONSTANT];
}

- (id) initWithNumberOfSides:(int)sides minimumNumberOfSides:(int)min maximumNumberOfSides:(int)max {
    if (self = [super init]) {
            // Cautiously initialize everything to zero
            numberOfSides = 0;
            minimumNumberOfSides = 0;
            maximumNumberOfSides = 0;

            // Attempt the actual assignment
            [self setMaximumNumberOfSides:max];
            [self setMinimumNumberOfSides:min];
            [self setNumberOfSides:sides];
    }

    return self;
}

- (NSString *) description {
    return [NSString stringWithFormat:@"Hello I am a %d-sided polygon (aka a %@) with angles of %.3f degrees (%.6f radians)", [self numberOfSides], [self name], [self angleInDegrees], [self angleInRadians]];
}

- (void) dealloc {
    NSLog(@"(Polygon): Dealloc is called");
    [super dealloc];
}

@end

综上所述,我认为问题是:

  1. 为什么不使用dealloc函数?我实际上在哪里有泄漏?
  2. 当我初始化我的 mainController 时,我是否应该将我在为 mainController 创建自定义类时创建的 nib 文件的名称作为参数传递?
  3. 我应该以不同的方式初始化 mainController 及其视图吗?
  4. 我应该使用 awakeFromNib 而不是 loadView 或 viewDidLoad?
  5. 您对整个方法还有其他意见吗?例如单屏应用程序的好或坏编程实践?这确实是一个小程序,所以,我相信这不是一个开放式的问题。但是,如果您认为这是一个开放式问题,请不要回答。这可能是一个菜鸟对一些老手提出的一个幼稚的问题。

我非常感谢您为此花费的所有时间,并期待您的反馈。

4

0 回答 0