Observation on Reference Object and interclass interaction

Discussion in 'iOS Programming' started by mistergreen2011, May 1, 2011.

  1. macrumors member

    Joined:
    Mar 23, 2011
    #1
    Hi,
    I'm fairly new so I'm still trying to figure out the nuances of objective C that I might have missed. Correct me if I'm wrong?

    I noticed this about referenced objects

    Code:
    //class A 
    @interface A {
         NSArray *myArray;
    }
         - (void)callMethod;
    @end
    
    @implementation
          //init sets up a uinavigation
         -(void)init {
             myArray=[[NSArray alloc] initWithObjects:@"Andy", @"Erik", @"Aaron", nil];
             [self.callMethod];
          }
    
         -(void)callMethod {
                NSlog(@"data %@", myArray);
         }
    @end
    
    //class B
    @interface B {
         - (void)callMethodFromA;
    }
    @end
    
    @implementation
         - (void)callMethodFromA {
             A *call = [[A alloc] init];
             [call callMethod];
             [call release];
         }
    @end
    
    
    Given this, when 'callMethod' is call from inside class A, it will print "Andy, Eric, Aaron"...

    When called from class B, it will print 'null'. This is what I'm seeing on a regular basis. The only way I've been able to resolve this is to create a protocol/delegate so calling from class B won't print out a 'null'

    Am I correct in the observation?
     
  2. chown33, May 1, 2011
    Last edited: May 1, 2011

    macrumors 603

    Joined:
    Aug 9, 2009
    #2
    Your code doesn't compile as given.

    EDIT:
    After fixing enough bugs to compile and run it, I don't see 'null' being printed:
    Code:
    #import <Foundation/Foundation.h>
    
    //class A 
    @interface A : NSObject {
         NSArray *myArray;
    }
         - (void)callMethod;
    @end
    
    @implementation A
         -(id)init {
    	self = [super init];  // CAUTION: doesn't check for nil here
    
             myArray=[[NSArray alloc] initWithObjects:@"Andy", @"Erik", @"Aaron", nil];
             [self callMethod];
    	return self;
          }
    
         -(void)callMethod {
                NSLog(@"data %@", myArray);
         }
    @end
    
    //class B
    @interface B : NSObject {
    }
         - (void)callMethodFromA;
    @end
    
    @implementation B
         - (void)callMethodFromA {
             A *call = [[A alloc] init];
             [call callMethod];
             [call release];
         }
    @end
    
    
    int main(int arcgc, char *argv[])
    {
    	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    	
    	B * myB = [[B alloc] init];
    	
    	[myB callMethodFromA];
    
    	[myB release];
    	
    	[pool drain];
    	return 0;
    }
    Command-line and output:
    Code:
    [B]gcc -framework Foundation -std=c99 dummy.m 
    ./a.out[/B]
    2011-05-01 19:10:03.541 a.out[33159:903] data (
        Andy,
        Erik,
        Aaron
    )
    2011-05-01 19:10:03.544 a.out[33159:903] data (
        Andy,
        Erik,
        Aaron
    )
    
     
  3. mistergreen2011, May 1, 2011
    Last edited: May 1, 2011

    thread starter macrumors member

    Joined:
    Mar 23, 2011
    #3
    yeah, thanks for taking the time making it work. It was pseudo code not actual code.

    hmmm. so I AM missing something.. I'll look into it.

    This is an actual code that prints out a null

    Code:
    #import <UIKit/UIKit.h>
    
    @class ReferenceTestViewController;
    
    @interface ReferenceTestAppDelegate : NSObject <UIApplicationDelegate> {
         NSArray *myArray;
    }
    
    @property (nonatomic, retain) IBOutlet UIWindow *window;
    
    @property (nonatomic, retain) IBOutlet ReferenceTestViewController *viewController;
    - (void)output;
    
    
    @end
    
    #import "ReferenceTestAppDelegate.h"
    #import "ReferenceTestViewController.h"
    
    @implementation ReferenceTestAppDelegate
    
    
    @synthesize window=_window;
    
    @synthesize viewController=_viewController;
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // Override point for customization after application launch.
         myArray=[[NSArray alloc] initWithObjects:@"Andy",@"Erik",@"Aaron",nil];
        
        self.window.rootViewController = self.viewController;
        [self.window makeKeyAndVisible];
        return YES;
    }
    
    - (void)output {
        NSLog(@"number %@", myArray);
        
    }
    
    ---------------
    
    #import <UIKit/UIKit.h>
    
    @interface ReferenceTestViewController : UIViewController {
        
    }
    
    @end
    
    
    #import "ReferenceTestViewController.h"
    #import "ReferenceTestAppDelegate.h"
    
    @implementation ReferenceTestViewController
    
    - (void)dealloc
    {
        [super dealloc];
    }
    
    - (void)didReceiveMemoryWarning
    {
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
        
        // Release any cached data, images, etc that aren't in use.
    }
    
    #pragma mark - View lifecycle
    
    
    // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        ReferenceTestAppDelegate *nav = [[ReferenceTestAppDelegate alloc] init];
        [nav output];
        [nav release];
        
    
    }
    
    
    
    
    
    
    
     
  4. macrumors 603

    Joined:
    Aug 9, 2009
    #4
    In your posted code, you have this (let's call it code-fragment A):
    Code:
        ReferenceTestAppDelegate *nav = [[ReferenceTestAppDelegate alloc] init];
        [nav output];
    
    In the ReferenceTestAppDelegate class you have this (fragment B):
    Code:
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // Override point for customization after application launch.
         myArray=[[NSArray alloc] initWithObjects:@"Andy",@"Erik",@"Aaron",nil];
    ...etc...
    }
    
    At what point in fragment A is application:didFinishLaunchingWithOptions: ever called? The answer: never.

    If application:didFinishLaunchingWithOptions: isn't called, is myArray ever assigned a value? Answer: no. How could it be otherwise? You only assign a value to myArray in the application:didFinishLaunchingWithOptions: method. No other method ever assigns a value to myArray.

    The direct consequence of all this is that myArray is not assigned a value until after application:didFinishLaunchingWithOptions: is called. If that method is never called, then myArray remains nil. Therefore, when you try to NSLog it, it's nil.

    If you were to send your instance the application:didFinishLaunchingWithOptions: message before the -output message, then myArray would be assigned a value, and output would show that non-nil value.


    The central problem is that you're not performing any of the life-cycle methods of the ReferenceTestAppDelegate instance, yet those methods are the only place where myArray is initialized. You can either call the life-cycle methods directly (only suitable for testing), or you can arrange for them to be called by setting an object as the actual delegate, or you can move the initialization of myArray into a method where it will always be performed.

    If you were using an instance of ReferenceTestAppDelegate as an actual app delegate, then the UIKit itself would be calling the life-cycle methods at the proper times. But since you didn't set your directly created instance of ReferenceTestAppDelegate as an actual app delegate, those things aren't happening. These life-cycle methods aren't magic, and they don't happen automatically. If an object isn't the actual app delegate, they won't happen.

    If you had used the actual app delegate object, instead of alloc'ing and init'ing a new and separate object, then you should have found that the life-cycle methods are invoked, and when your app has finished launching, then myArray will have a valid value. The actual app delegate is the one returned by [UIApplication sharedInstance].delegate.

    Another option would be to define a real -init method in your ReferenceTestAppDelegate class, and assign an array to myArray in that method. Then no matter what happens after that point, life-cycle methods or not, the array will have a valid value.

    If that doesn't make sense, then I suggest you study the proper usage of init methods and object creation. The "Allocating and Initializing Objects" section of the Objective-C Programming Language reference covers this:
    http://developer.apple.com/library/...nceptual/ObjectiveC/Chapters/ocAllocInit.html

    I also suggest studying delegates and the delegation pattern, covered in the "Cocoa Design Patterns" section of the Cocoa Fundamentals Guide:
    http://developer.apple.com/library/.../CocoaDesignPatterns/CocoaDesignPatterns.html

    Also make sure you understand the difference between a class (such as ReferenceTestAppDelegate), and an instance of that class. You can create multiple instances of a class like ReferenceTestAppDelegate, but only one object can ever be the actual delegate object returned by the delegate property of UIApplication's +sharedInstance method.

    Finally, this isn't "interclass interaction" as your thread title indicates. It's inter-object interaction, and there's a big difference. Understanding the difference between a class and an object that's an instance of a class is crucial to understanding all of Objective-C and how it provides object-oriented programming. You would benefit from reading all of the Cocoa Fundamentals Guide, and the early chapters of the Objective-C Programming Language Guide, instead of just the isolated sections I pointed out.
     
  5. thread starter macrumors member

    Joined:
    Mar 23, 2011

Share This Page