Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

larswik

macrumors 68000
Original poster
Sep 8, 2006
1,552
11
Everything was going well tonight and I got that error message. "Error: Duplicate interface Definition for Class' CharacterStats' "

I looked at the gray'd '!' under the error message and they are highlighting the #import's that I have. Can you over #import classes? As I am refining my code from main.m I am putting things in to their own classes to help un- clutter things.

Lets say I have 4 Classes 'AClass', 'BClass', 'CClass' & 'DClass'.

In both 'BClass' & 'CClass' I use #import"AClass.h"

Now in 'DClass' I add the other 3
#import"AClass.h"
#import"BClass.h"
#import"CClass.h"

I am guessing this is bad since I am doubling up on these inheritance and not taking in to account inheritance?

While writing this I reasoned this out but have not tried to clean it up yet to test it but I think it is because everything gets inherited.

Thanks
 
Yep, Inheritance problem. I cleaned up the #imports and that fixed it.

Thanks.
 
Do you define CharacterStats is more than one .h file? Have you used #include somewhere instead of #import. The reason I ask because the answer to your question is no, you can't over #import.

With #import a file is read in only once per compilation unit. So that fact in DClass.m you #import "AClass.h" and so does BClass.h and C.Class.h should have been fine. The #import "AClass.h" in BClass.h and CClass.h should have been no-ops because AClass.h should have already been imported.

It's a different story with #include. #include doesn't consider if the file has already been included elsewhere. These kinds of duplicate definition errors are common with starting C programmers (C doesn't have #import) for this reason.
 
I went through and cleaned up the #imports and it solved the problem. I am working on a larger project and as I am working on it I am finding ways to simply it and creating new classes. I am surprised that you can't over #import because that fixed the problem when I started deleting them. I had the problem again shortly after when I had a Fighter Class and had the ivars 'level', 'hitpoints' and so on. I then created 2 more classes called BadGuyArmy Class and GoodGuyArmy Class and used the #import"fighter.h" for both the Good and Bad army were almost identical and copied the methods from 1 to another and had just slight alterations to the code.

In Main.m I #imported all 3 'Fighter.h', 'GoodGuyArmy.h' and 'BadGuyArmy.h'. It gave me a bunch of errors when I tried to created the army with a for loop that were linked all the way back to the ivars in the 'Fighter' Class. I solved the problem by deleting both bad and good army classes and create a new class called 'CreateArmy' as follows bellow. When control when back to main.m they were put into the NSArray you helped me with the other day.

Code:
#import <Foundation/Foundation.h>
#include "fighterClass.h"
#include "CharacterStats.h"
#include <stdlib.h>


@interface CreateArmys : NSObject {
@private
    
}
-(id)makeRecuritGoodSide: (int) Level withID: (int) nameNumber;
-(id)makeRecuritBadSide: (int) Level withID: (int) nameNumber;
-(void)printFighter: (id) fighter withFighterLevel: (int) theLevel;
@end
Code:
#import "CreateArmys.h"


@implementation CreateArmys

-(id)makeRecuritGoodSide: (int) Level withID: (int) nameNumber{
    fighterClass *aFighter = [[fighterClass alloc]init]; // instantiate a fighter from the fighterClass
    CharacterStats *getStatsForFighter = [[CharacterStats alloc]init];
    
    [aFighter setFighterId: nameNumber];
    [aFighter setLevel: Level];
    [aFighter setHitPoints: [getStatsForFighter findHitPoints:[aFighter level]]];
    [aFighter setArmorType:[getStatsForFighter findArmorType:[aFighter level]]];
    [aFighter setAdrenalSpeed: [getStatsForFighter findAdrenalSpeed:[aFighter level]]];
    [aFighter nameLetter:@"G"]; 
    return aFighter;
    [aFighter release];
    [CharacterStats release];
}
-(id)makeRecuritBadSide: (int) Level withID: (int) nameNumber{
    fighterClass *bFighter = [[fighterClass alloc]init]; // instantiate a fighter from the fighterClass
    CharacterStats *getStatsForFighter = [[CharacterStats alloc]init];
    
    [bFighter setFighterId: nameNumber];
    [bFighter setLevel: Level];
    [bFighter setHitPoints: [getStatsForFighter findHitPoints:[bFighter level]]];
    [bFighter setArmorType:[getStatsForFighter findArmorType:[bFighter level]]];
    [bFighter setAdrenalSpeed: [getStatsForFighter findAdrenalSpeed:[bFighter level]]];
    [bFighter nameLetter:@"B"]; 
    return bFighter;
    [bFighter release];
    [CharacterStats release];

}

-(void)printFighter: (id) fighter withFighterLevel: (int) theLevel{
    NSLog(@"%@-ID: %d",[fighter returnNameLetter ],[fighter fighterId]);
    NSLog(@"LVL: %d",theLevel);
    NSLog(@"HP: %d",[fighter hitPoints]);
    NSLog(@"AT: %d",[fighter armorType]);
    NSLog(@"AS: %d",[fighter adrenalSpeed]);
    NSLog(@"-");
}

@end

I am starting to get the hang of it. I had this line of code that worked just fine in the previous version. I am seeing how things are working now.

Code:
[aFighter setHitPoints: [swordAttackTable getDamageRoll:dieRoll getEnemyArmorType:goodGuyArmorType gotAttackersAdrenalSpeed:badGuyAdreanalSpeed getEnemysHitPoints:[aFighter hitPoints] getName:[bFighter returnNameLetter]]];
 
Code:
#import <Foundation/Foundation.h>
[COLOR=red]#include[/COLOR] "fighterClass.h"
[COLOR=red]#include[/COLOR] "CharacterStats.h"
#include <stdlib.h>

You are using #include, not #import.

And you know your CreateArmys class is bad from OOAD perspective because its name reads as a verb and not as a noun.
 
Last edited:
I am glad I posted the code. I made the mistake of using the #import for much of the code and it has not complained? I will change them now but I am curious why I didn't even get warnings?

Thanks for catching that.
 
Code:
-(id)makeRecuritGoodSide: (int) Level withID: (int) nameNumber{
    fighterClass *aFighter = [[fighterClass alloc]init]; // instantiate a fighter from the fighterClass
    CharacterStats *getStatsForFighter = [[CharacterStats alloc]init];
    
    [aFighter setFighterId: nameNumber];
    [aFighter setLevel: Level];
    [aFighter setHitPoints: [getStatsForFighter findHitPoints:[aFighter level]]];
    [aFighter setArmorType:[getStatsForFighter findArmorType:[aFighter level]]];
    [aFighter setAdrenalSpeed: [getStatsForFighter findAdrenalSpeed:[aFighter level]]];
    [aFighter nameLetter:@"G"]; 
    return aFighter;
    [COLOR=red][aFighter release];
    [CharacterStats release];[/COLOR]
}

The code in red above never executes. Do you know why?



I made the mistake of using the #import for much of the code and it has not complained? I will change them now but I am curious why I didn't even get warnings?

You should be using #import. You should not be using #include.

It's in no way an error to use #include, there is no warning to be had. The problem is when a declaration gets #include'ed multiple times. But the error is in the multiple declarations, not in the multiple #include's, which in other context may be perfectly valid.
 
You have me scratching my head on this one with my 2 releases that never get exicuted. I used the NARC approach so I took ownership of them so it should be my responsibility to release them.

But since you say they never get executed I will have to think back to my C and Pascal book and class. That rule was anything I declare in a Function gets destroyed when it exits the Function automatically . So even though I used the NARC approach for those objects in the method, they get destroyed automatically?
 
You have me scratching my head on this one with my 2 releases that never get exicuted. I used the NARC approach so I took ownership of them so it should be my responsibility to release them.

But since you say they never get executed I will have to think back to my C and Pascal book and class. That rule was anything I declare in a Function gets destroyed when it exits the Function automatically . So even though I used the NARC approach for those objects in the method, they get destroyed automatically?

Take the debugger, set a breakpoint on the first line of the method, then step through one line after the other and see what happens. (Bugs are usually not where you expect them to be).
 
Not suggesting for a moment to not use #import, however, if you were writing this in C or C++ and wanted to guard against over-including, then you would make sure your .h files look something like this:

Code:
#ifndef _myfile_h
#define _myfile_h

// normal .h contents
...

#endif // _myfile_h

Basically this declares a macro "_myfile_h" (e.g., "_fighterClass_h") which only includes the contents the first time the preprocessor sees the .h file when compiling a single .c or .cpp file.
 
I set up the breakpoints for each step and and watched it. Everything populated correctly and but after the 'return aFighter' it did not stop at the 2 releases that I set up. Is this because when it saw return it ended that method?
 
I set up the breakpoints for each step and and watched it. Everything populated correctly and but after the 'return aFighter' it did not stop at the 2 releases that I set up. Is this because when it saw return it ended that method?

bingo
 
So then the question I have is how can I release them? I took ownership of them with the Alloc and I need to release them unless they get released by them self's when the Method exits. If I release them before I return the object technically they return nothing then?

-Lars
 
It sounds like you need to make an agreement between the caller and the callee that the caller will own/be responsible for these objects going forward. I.e., it becomes the caller's responsibility to free these objects when it no longer needs them.
 
It sounds like you need to make an agreement between the caller and the callee that the caller will own/be responsible for these objects going forward. I.e., it becomes the caller's responsibility to free these objects when it no longer needs them.

The memory management rules are clear here. The caller has no responsibility for releasing the returned object. It is responsible for retaining the object if it wants to keep it around. The callee is responsible for arranging for the returned object to be released after giving the caller a chance to retain it first.


So then the question I have is how can I release them? I took ownership of them with the Alloc and I need to release them unless they get released by them self's when the Method exits. If I release them before I return the object technically they return nothing then?

-Lars

You autorelease an object you want to return from a non-NARC method. That's the primary reason autorelease was created. Assuming you haven't created your own autorelease pools, then the release your method owes won't be sent until the end of the current event. This should be delayed long enough for the caller to retain the returned object if it wants to keep it around.

So change the code to this:
Code:
-(id)makeRecuritGoodSide: (int) Level withID: (int) nameNumber{
    fighterClass *aFighter = [[fighterClass alloc]init]; // instantiate a fighter from the fighterClass
    CharacterStats *getStatsForFighter = [[CharacterStats alloc]init];
    
    [aFighter setFighterId: nameNumber];
    [aFighter setLevel: Level];
    [aFighter setHitPoints: [getStatsForFighter findHitPoints:[aFighter level]]];
    [aFighter setArmorType:[getStatsForFighter findArmorType:[aFighter level]]];
    [aFighter setAdrenalSpeed: [getStatsForFighter findAdrenalSpeed:[aFighter level]]];
    [aFighter nameLetter:@"G"]; 
    [COLOR=red][CharacterStats release];[/COLOR]
    [COLOR=blue]return [aFighter autorelease];[/COLOR]
}

But there's still a problem with the line of code in red. Can you see it?
 
Last edited:
The memory management rules are clear here. The caller has no responsibility for releasing the returned object. It is responsible for retaining the object if it wants to keep it around. The callee is responsible for arranging for the returned object to be released after giving the caller a chance to retain it first.

I was hesitating to even respond, because I knew this related to Objective C... apologies for misleading anyone in my answer as I naively responded. Time to learn Objective C... :)
 
I never saw 'autorelease' before, it was not in the books I have read. Thanks for explaining that. As for the part in red, it took me a few minutes but I saw my mistake. It should be [getStatsForFighter release]; and not [CharacterStats release]; I need to release the object and not the class. That was a dumb mistake but still I got no error message when I ran the program, hummm

Thanks!
 
I never saw 'autorelease' before, it was not in the books I have read. Thanks for explaining that.

To learn more about autorelease and autorelease pools read
  • Chapter 17 of Kochan
  • Chapter 9 of Learn Objective-C on the Mac (I think you've got this one?)
and/or search autorelease in the Apple docs


As for the part in red, it took me a few minutes but I saw my mistake. It should be [getStatsForFighter release]; and not [CharacterStats release]; I need to release the object and not the class. That was a dumb mistake but still I got no error message when I ran the program, hummm

You're lucky if this kind of mistake causes an error at the place in the program where the mistake is. More usually things start going wrong elsewhere, and it takes fair amount of forensic work to trace the symptoms back to the problem.
 
I have both of those books. I started 'Learn Objective-C on the Mac' but switched to Learn Objective -C for Absolute beginners from the same company. I have never really applied any C skills I learned before. When I finished the C book I started my Pascal Class a week later. Chapter 3 in Learn Objective - C on the Mac is where I started getting lost as they started to cover how to convert a C project to Obj-C. I switched to the easier book.

Now in Summer I started to tackle Objective-C before my Java class starts August 22nd.

I will skip to chapter 9 and read up on it this weekend.

Right now my [pool drain]; I get a 'EXC BAD ACCESS'. I am slowly walking through my code in main and the 5 different classes to find the problem.

Thanks
 
@larswik, Have you read the Kochan book? It's excellent. I've read a few of the editions, and they are all really great. I struggled with trying to teach myself Objective-C until I read Kochan's book.

Have you been using Xcode's "Build and Analyze" button? It's a phenomenal debugging/learning tool.

I'm happy to hear you're embracing the Object Oriented paradigm. Are you beginning to see it's advantages (such as encapsulation)?

As an aside @jiminaus is absolutely correct about your CreateArmys class being named wrong. It should be along the lines of ArmySetup or ArmyCreator or something like that. Having your classnames be verbs is confusing, because they look like they should be method calls.
 
Last edited:
Many years ago I started with python till mom passed away. I stopped for a couple of years to deal with that. I then picked up Kochan's book but got lost about 1/3 when he started using 'self'. I even spent $75 to download his videos to go with the book. Objective - C was to much to soon. I learned C and then the Pascal class and then had a good understanding of how things worked.

Compared to just C, Objective C is massive. But the more I understand the Object Oriented paradigm, the more I see how things work together. The greatest thing for learning has been to tackle my own projects with what I have learned. Then you guys have been amazing filling in the gaps.

I believe you (GorillaPaws) pointed out in my last project that I did not take advantage of the power of Objective C and wrote my last program as 1 long procedural code in the AppController. This project was to put more work load in to classes. I do see the benefits of Objective C now.

As far as the class naming I see your point. I was trying to name them a more meaningful but ArmyCreator is more representative then CreateArmy. I have been asking myself when naming a class, "What does this class do" we'll.... "It creates an army, so I will call it CreateArmy", that has been my approch so far.

Thanks
 
"What does this class do?" tells you the behaviour of the class. That is what methods it will have and what those methods will do.


There are blueprints out there called design patterns. These design patterns tell you what classes, what methods in those classes, and what relationship between those calsses are need in a particular design.

Cocoa is full of design patterns. Examples include small things like delegates, outlets, targets/actions; bigger things like notifications, key-value observing and key-value binding; and massive things like the model-view-controller architecture and the document architecture.

One of the books I found most useful while learning Cocoa was Buck & Yacktman, Cocoa Design Patterns, 2009, Addison-Wesley Professional. It teaches the design patterns used by Cocoa instead of teaching individual techniques. This phenomenally improved me as a Cocoa programmer because I had the prior knowledge needed to forge into new areas of Cocoa and learn straight off the developer docs.

The most famous set of design patterns are the ones published in Gamma, et al., Design Patterns: Elements of Reusable Object-Oriented Software, 1994, Addison-Wesley Professional.


Why am I mentioning all of this? Because what you're implementing here is a very basic version of the Factory design pattern. So call your class ArmyFactory.

But wait. I don't think you need a separate class at all for this. Notice that your class has no instance variables. Notice that the methods in this class are really all about figherClass. These methods should be in fighterClass.


The printFighter method looks to be a good candidate for being implemented as an override of the description method. It could be implemented as follows.

Code:
@implementation fighterClass

- (NSString *)description
{
    NSMutableString str = [[[NSMutableString alloc] init] autorelease];
    [str appendFormat:@"%@-ID: %d\n", [fighter returnNameLetter ], [fighter fighterId]];
    [str appendFormat:@"LVL: %d\n", [fighter level]];
    [str appendFormat:@"HP: %d\n", [fighter hitPoints]];
    [str appendFormat:@"AT: %d\n", [fighter armorType]];
    [str appendFormat:@"AS: %d\n", [fighter adrenalSpeed]];
    [str appendString:@"-"];
    return str;
}

@end

Then it any figherClass object can logged with just:
Code:
NSLog("%@", aFigherClassObject);


In regards to the methods, what are these methods actually doing? They're allocating and initialising figherClass objects. How do you typically allocate and initialise an object? By either using alloc/init or using a convenience constructor. So let's try reimplementing the first of these methods these as a convenience constructor.

Code:
@interface fighterClass
[COLOR=blue]+[/COLOR] ([COLOR=blue]fighterClass *[/COLOR])[COLOR=blue]fighter[/COLOR]OnGoodSideWithLevel:(int)level withID:(int) nameNumber;
@end

@implementation fighterClass
[COLOR=blue]+[/COLOR] ([COLOR=blue]fighterClass *[/COLOR])[COLOR=blue]fighter[/COLOR]OnGoodSideWithLevel:(int)level withID:(int) nameNumber
{
  // Body of old makeRecuritGoodSide:withID: here
}
@end

You can do similarly for the bad side and then a fighter can be created like so:
Code:
figherClass *goodFighter = [fighterClass fighterOnGoodSideWithLevel:10 withID:1]


Is this all feeling more Cocoa-like? I'm using Cocoa's design patterns to implement your code.


We can go even further with these convenience constructors, and turn them into regular, straight initialisers with the advantage of not having to deal with autorelease.

Code:
@interface fighterClass
[COLOR=green]-[/COLOR] ([COLOR=green]id[/COLOR])[COLOR=green]init[/COLOR]OnGoodSideWithLevel:(int)level withID:(int) nameNumber;
@end

@implementation fighterClass
[COLOR=green]-[/COLOR] ([COLOR=green]id[/COLOR])[COLOR=green]init[/COLOR]OnGoodSideWithLevel:(int)level withID:(int) nameNumber
{
    self = [super init];
    if (self) {
        CharacterStats *getStatsForFighter = [[CharacterStats alloc] init];
    
        [self setFighterId: nameNumber];
        [self setLevel: level];
        [self setHitPoints: [getStatsForFighter findHitPoints:[self level]]];
        [self setArmorType:[getStatsForFighter findArmorType:[self level]]];
        [self setAdrenalSpeed: [getStatsForFighter findAdrenalSpeed:[self level]]];
        [self nameLetter:@"G"]; 

        [getStatsForFighter release];
    }
    return self;
}
@end

An now creating a figherClass object is really normal:
Code:
figherClass *goodFighter = [[fighterClass alloc]  initOnGoodSideWithLevel:10 withID:1]
 
Last edited:
As an Amazon Associate, MacRumors earns a commission from qualifying purchases made through links in this post.
Much of that code I have not seen yet. I have not worked with any 'Class Mentods'. + as of yet.

"How do you typically allocate and initialise an object? By either using alloc/init or using a convenience constructor." - I have never even heard of convenience constructors.

As far as Design Patterns I don't know if I should start the Kochan book or design pattern book you mentioned? I guess looking at the project I uploaded now will give you an idea of how I am coding.

I decided to compress and upload the War Simulator so you can see my code. Right now it just populates the Army's for good and bad, and it works, but no combat. You can set an army's average level on line 27 and 28 of main.m to populate higher or lower leveled people. Up top I #defined the number of fighters as the size of the army. I also have a .txt for the damage for combat, but removed that a few days ago when I was cleaning up code and have not put it back yet. If you play RoleMaster then you know what armor types, hit points, adrenal speed bonus and levels are.

The goal this project is for my friends who still game to use, and I learn more about how to use objects from classes to simplify my code in main. GorillaPaws made mention of it, that I was not taking advantage of objects. with my calc program. There are probably easier ways to do the same ting with less code, but I don't know those ways yet. I notated everything pretty good.

If anyone wants to critique it and point out any glaring errors (even though the code works) I would greatly appreciate comments.

Thanks
 

Attachments

  • oneOnOnev2.zip
    48.9 KB · Views: 152
I disagree a bit with @jiminaus' suggestion. I confess however that might misunderstand the direction of what your app is trying to do, but based on what I'm guessing you might be trying to accomplish I would take a somewhat different approach.

I would create a PlayerUnit class (or NPCUnit Class--I'm not quite sure what you're using these fighter objects for exactly). This class would have methods like setLevel:, and setHitPoints:, basically setting all of attributes and behaviors that all of your classes will have in common. Next I would create a Fighter class that would inherit from the PlayerUnit class, as well as a Mage class, a Thief Class (etc.) or whatever other classes you're planning on implementing. All of these classes would inherit from the PlayerUnit Class. The individual Fighter, Mage, etc. classes would each implement the items SPECIFIC to just those classes that make them unique. As an example, your mage class might have a mana bar with a maxMana property and a currentMana property. This wouldn't apply to all classes so it should go in the Mage Class instead of the generic PlayerUnit parent class that all of the other classes would inherit from. The Thief class might have a stealthRating property, and so that would be implemented in the Thief class only.

Here's why this system is good: if for example, you change the way ArmorRating is calculated, you only need to change that method in 1 place (the PlayerUnit Class), because all of the other classes inherit from it. If you were to instead, not have a common Parent class and copy/paste the ArmorRating calculation method into the Fighter class, Mage Class etc. Then you would need to make those changes in several places. When you're duplicating methods in multiple places, then there's a good chance that you can make a mistake somewhere. This is ALWAYS a sign that there's something wrong with the code and is an example of what is commonly referred to as a Code Smell.

@jiminaus is correct that what you're doing falls into the constructor pattern and belongs in an init method, but I disagree that all of these stats should be set within the Fighter class. I would do something like this:
Code:
@interface PlayerUnit: NSObject
{
    // properties that all units would have
    NSString     *_name;
    BOOL         _isEvil;
    NSInteger    _hitPoints;
    NSInteger    _level;
    //  etc. you get the idea
}
//  This is the designated initializer
-(id) initWithName: (NSString *)theName Evil: (BOOL)isBadGuy hitPoints: (NSInteger)theHitPoints level: (NSInteger)theLevel;  //etc. for all of the properties.

Now here's an example of the interface of a Mage class:
Code:
@interface MageClass: PlayerUnit  //notice it inherits from the player unit class gaining all of it's ivars and methods so mages can do everything a PlayerUnit can do plus what we're about to add below
{
    NSInteger           _maxMana;
    NSInteger           _currentManaLevel;
    NSMutableArray      *_spellbook;
    //  notice how these attributes wouldn't make sense in a Fighter Class, or in the parent class.  Because they're really only useful to a mage they belong here.
}
//  This is an connivence constructor method below
+(MageClass *) mageWithName: (NSString *)theName evil: (BOOL)isBadGuy hitPoints: (NSInteger)theHitPoints level: (NSInteger)theLevel maxMana: (NSInteger)theMaxMana;

In the implementation of my mage class method I would do something like this:

Code:
@implementation MageClass

+(MageClass *) mageWithName: (NSString *)theName evil: (BOOL)isBadGuy hitPoints: (NSInteger)theHitPoints level: (NSInteger)theLevel maxMana: (NSInteger)theMaxMana
{
     MageClass *newMage = [[MageClass alloc] initWithName: theName 
                                                     evil: isBadGuy 
                                                hitPoints: theHitPoints 
                                                    level: theLevel
                                                  maxMana: theMaxMana];
     [newMage autorelease];
     return newMage;
}

-(id) initWithName: (NSString *)theName evil: (BOOL)isBadGuy hitPoints: (NSInteger)theHitPoints level: (NSInteger)theLevel maxMana: (NSInteger)theMaxMana
{
    //  Calls Super's Designated Initializer--i.e. PlayerUnit's initWithName: evil: hitPoint: method and passes in the appropriate parameters 
    if((self = [super initWithName: theName 
                              evil: isBadGuy 
                         hitPoints: theHitPoints 
                             level: theLevel])) 
    {
      _maxMana = theMaxMana;
      _currentMana = _maxMana;   //  New characters always start with full mana
      
        //set up other iVars with initial default values.
    }
    return self;
}

To instantiate a MageClass object, you could either call alloc and our custom init, or use the the Class method constructor here are examples of both:
Code:
// in some code where you want to instantiate your MageClass object:
MageClass *goodMage1 = [[MageClass alloc] initWithName: @"Sloth" 
                                                  evil: NO
                                             hitPoints: 45 
                                                 level: 2
                                               maxMana: 120];

//  or you can use the convenience constructor which means it saves you from calling the alloc and returns an autoreleased instance.
MageClass *evilMage1 = [MageClass mageWithName: @"Chunk" 
                                          evil: YES
                                     hitPoints: 45 
                                         level: 4
                                       maxMana: 120];
[evilMage1 retain];  //  we might want to retain this because it's autoreleased and will go away if we don't take ownership of it

As far as books are concerned, I would read them in the following order:
1. Kochan - If you haven't mastered the concepts here, then the design patterns book will be much less valuable (because it's a bit tricky in some places).

2. Clean Code This one is great for understanding why some code is designed/structured better than others. I wish that I had read this book much earlier in my studies than I did. It moves quickly, and you don't have to necessarily get too into the gnity-gritty. It's also available on the iBook store, so you can preview the first chapter or two for free.

3. Cocoa Design Patterns - It's an excellent book, but I think you'll be able to get more from it if you have a very solid understanding of Obj-C and have a general sense of the basic strategies for writing good, simple and clean Object Oriented Code. Once you've got that foundation, this book will click more for you.

To approach it more metaphorically, Kochan is like learning basic english grammar, and Clean Code is like learning how to write good paragraphs and understanding why one sentence is better structured than some other one even if both are grammatically correct. The Design Patterns book would be like learning the common literary techniques for writing some particular genre of writing.

This is just my opinion, so feel free to take it with a grain of salt. Everyone learns differently. I am by no means an expert.
 
Last edited:
As an Amazon Associate, MacRumors earns a commission from qualifying purchases made through links in this post.
Sorry, don't worry about studying design patterns for now, that's for later. Continue with Cocoa Recipes for Mac OS X so in the end you see what it's like to complete a full project. Then try Kochan again now that you'd be wiser from experience. Then may be look at the Cocoa Design Patterns book.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.