开发者

Objective-C syntax - how to initialize an array that "lives" deep in an object hierarchy

I think this sketch will add clarity to what I am trying to achieve.

Essentially, I want to create iPhone app that allows user to select a schoolDistrict from a tableview and the app will slide that view over and reveal the next tableview full of schools. When they select a school, the next view displays the teachers, finally, when they select a teacher, it will display the courses taught by that teacher.

So, I am trying to construct some sample data for my viewtable "drilldown" and I am struggling to populate such a hierarchical data structure.

I'm hoping the sketch below helps.

https://skitch.com/aibkwik/rqu41/photo-feb-13-10-30-15-pm.jpg


original post follows:

UPDATED: I am trying to alloc an array that is deeply embedded in an object hierarchy. The top object instance is call "thisYear" and it has an array in it called "courses". "courses" also contains, among other things, an array called scoreCatetories. How would you initialize and add an object to the "scoreCategories" array? I've tried numerous things, including:

I'm trying all manner of combinations such as the one below - nothing is working.

...
[[thisYear courses] scoreCategories] = [[NSMutableArray alloc] init];
[[[thisYear courses] scoreCategories] addObject:myNewObj]; 
...

I'm trying to load some sample data - the code below is located in the didFinishLaunchingWithOptions function of the appDelegate.

Here is the code snippet that is causing me grief. Please see the .h files, further down in this post.

dGradeURin *thisYear;
thisYear = [[dGradeURin alloc] init];
[thisYear.howManyTerms initWithInteger: 4];
thisYear.gradeURin = @"Freshman";
//this paragraph compiles and runs fine
dCourse *tmpcourse;
tmpcourse = [[dCourse alloc] init];
tmpcourse.cName =@"Computer Science";
tmpcourse.school =@"Freedom High";
thisYear.courses = [[NSMutableArray alloc] init];
[thisYear.courses addObject:tmpcourse];


dScoringCategory *tmpSC;
tmpSC =  [[dScoringCategory alloc] init];
tmpSC.name = @"Quizzes";

//these two lines are not working
[[thisYear courses] setScoreCategories:[[[NSMutableArray alloc] init] autorelease]];
[[[thisYear courses] scoreCategories] addObject:tmpSC];     

//both the above lines compile with a warning:  NSMutableArray may not respond to -setScoreCategories
// when I run the program, the first line causes crash with an exception... See stack trace at far bottom

Here are the .h header file snippets for each interface object definitions, in essence...

@interface dGradeURin : NSObject {
    
    NSNumber *howManyTerms;
    NSString  *gradeURin;
    
    NSMutableArray *courses;
}

@property (retain, nonatomic) NSNumber *howManyTerms;
@property (retain, nonatomic) NSString *gradeURin;
@property (retain, nonatomic) NSMutableArray  *courses;

    @interface dCourse  : NSObject {
        NSString *cName;
        NSString *teacher;
        NSString *school;
        NSString *school_term;
        NSString *gradingMethod;
        NSNumber *whichterm;
        
        NSMutableArray  *scoreCategories;
    }
    
    @property (retain, nonatomic) NSString *cName;
    @property (retain, nonatomic) NSString *school;
    @property (retain, nonatomic) NSMutableArray *scoreCategories;
    
    @interface dScoringCategory : NSObject {
    NSString *name;
    NSMutableArray *scores;
}

@property (retain, nonatomic) NSString *name;
@property (retain, nonatomic) NSMutableArray *scores;

@interface dScore : NSObject {

    NSNumber    *score;
    NSDate      *scoreDate;
    NSString    *description;

}

@property (retain, nonatomic) NSDate *scoreDate;
@property (retain, nonatomic) NSNumber *score;
@property (retain, nonatomic) NSString *description;

Here is the stack trace

2011-02-13 21:49:43.559 GradeJanx[86526:40b] -[__NSArrayM setScoreCategories:]: unrecognized selector sent to instance 0x4b76660
2011-02-13 21:49:43.561 GradeJanx[86526:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM setScoreCategories:]: unrecognized selector sent to instance 0x4b76660'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x00db0be9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x00f055c2 objc_exception_throw + 47
    2   CoreFoundation                      0x00db26fb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3   CoreFoundation                      0x00d22366 ___forwarding___ + 966
    4   CoreFoundation                      0x00d21f22 _CF_forwarding_prep_0 + 50
    5   GradeJanx                           0x00002c50 -[AppDelegate_iPhone application:didFinishLaunchingWithOptions:] + 881
    6   UIKit                               0x002ba1fa -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1163
    7   UIKit                               0x002bc55e -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 439
    8   UIKit                               0x002c6db2 -[UIApplication handleEvent:withNewEvent:] + 1533
    9   UIKit                               0x002bf202 -[UIApplication sendEvent:] + 71
    10  UIKit                               0x002c4732 _UIApplicationHandleEvent + 7576
    11  GraphicsServices                    0x016e6a36 PurpleEventCallback + 1550
    12  CoreFoundation                      0x00d92064 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52
    13  CoreFoundation                      0x00cf26f7 __CFRunLoopDoSource1 + 215
   开发者_JS百科 14  CoreFoundation                      0x00cef983 __CFRunLoopRun + 979
    15  CoreFoundation                      0x00cef240 CFRunLoopRunSpecific + 208
    16  CoreFoundation                      0x00cef161 CFRunLoopRunInMode + 97
    17  UIKit                               0x002bbfa8 -[UIApplication _run] + 636
    18  UIKit                               0x002c842e UIApplicationMain + 1160
    19  GradeJanx                           0x000028bc main + 102
    20  GradeJanx                           0x0000284d start + 53


Okay, this is all sorts of messed up, so let's just step back a little bit. An array is an indexed set of objects. An index is a non-negative integer that represents the location of an object. Each object is accessible in the array by an index. You can't do [[thisYear courses] scoreCategories] and expect anything to work, because you haven't actually provided an index to the object in the array. You could do something like [[thisYear courses] objectAtIndex:0], which would return the first element in the array. You could also do [[thisYear courses] objectAtIndex:1], which would return the second element in the array.

Whenever you insert an element into an NSMutableArray, it inserts it as the last object in the array. For instance, if the NSMutableArray you're inserting your object into doesn't have any elements contained within it, the index of the element you're inserting is 0. If it have 1 element in it, then the index of the element you're inserting would have an index of 1.

The code you're probably looking for is this:

NSMutableArray *scoreCategories = [NSMutableArray array];
[scoreCategories addObject:myNewObj];
[[thisYear courses] addObject:scoreCategories];

However, there are a lot of other problems with doing things this way. Before proceeding any further, I'd recommend you spend some time with a good Objective-C book.

Update

Okay, let's take another look at this. This is pretty tough without the context in which this code appears, but we'll try anyway.

dGradeURin *thisYear = [[[dGradeURin alloc] init] autorelease];

thisYear.howManyTerms = [NSNumber numberWithInteger:4];
thisYear.gradeURin = @"Freshman";

NSMutableArray *courses = [NSMutableArray array];

//set up a temporary course
dCourse *temporaryCourse = [[[dCourse alloc] init] autorelease];
temporaryCourse.cName = @"Computer Science";
temporaryCourse.school = @"Freedom High";
temporaryCourse.scoreCategories = [NSMutableArray array];

dScoringCategory *temporaryScoringCategory = [[[dScoringCategory alloc] init] autorelease];
temporaryScoringCategory.name = @"Quizzes";

[temporaryCourse.scoreCategories addObject:temporaryScoringCategory];

[courses addObject: temporaryCourse];
thisYear.courses = courses;

This first line creates an autoreleased object. That means that at some point, the memory this object is taken up will be freed for use somewhere else. If you don't free the memory of an object, then it will keep taking up memory, whether you ever use it or not. For more about this (and you really need to understand it to use Objective-C), check out Apple's documentation.

A couple of other things. If this is homework, and it looks like it might be from the content, then be sure you tag it as homework. I don't mind helping you here because it looks like most of your problems are syntactic, but don't forget next time.

Second, I don't know where you got those classes, but assuming you're using the interfaces correctly, they appear to be poorly designed. There's a lot of understanding required to make things work well rather than just work. I'd still recommend reading that book I linked above if you really want to learn this stuff.


If it's a declared property (with the @property syntax), then you can use the dot notation to set the property value:

// This assumes that scoreCategories has either the 'retain' or 'copy' property;
// if it instead uses 'assign', then you'll need to retain this array.
[thisYear courses].scoreCategories = [NSMutableArray arrayWithObjects:myNewObj, nil];

If it's not a declared property, just access it using the standard arrow notation:

[thisYear courses]->scoreCategories = [[NSMutableArray alloc] initWithObjects:myNewObj, nil];


Not the cleanest approach, but anyway:

[[thisYear courses] setScoreCategories:[[[NSMutableArray alloc] init] autorelease]];
[[[thisYear courses] scoreCategories] addObject:myNewObj]; 


Something like

for (Course *course in thisYear.courses) {
  course.scoreCategories = [[[NSMutableArray alloc] init] autorelease];
}


I hate being that guy, but this kind of deep inspection (borrowed term there, hah!) smells like bad design to me.
Here's what i would do:
Have a property courses of the thisYear object that get's populated when needed. courses is an array that contains Course objects. The Course object has a property called scoreCategories and also a Method addScoreCategory. So for your code above, you'd do a

 Course * currentCourse = [[thisYear courses] objectAtIndex:0]; // assuming you want the first course
  if ([currentCourse isKindOfClass:[Course class]]) {
    [currentCourse addScoreCategory:myNewObj];
  }

In either the addScoreCategory or the init method of the Course class you'd create an NSMutableArray and assign it to the scoreCategories property.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜