hi i'm wondering category in objective-c
i have 3 files
A_ViewController
A_ViewController+Category
B_ViewController
here's example code
1-1. A_ViewController.h
#interface A_ViewController {
// some instance Variables
}
//some public methods
#end
1-2 A_ViewController.m
#import "A_ViewController.h."
#implementation A_ViewController
// implementation public methods and private methods
#end
2-1. A_ViewController+Category.h
#interface A_ViewControler(Category)
-(void) categoryMethod;
#end
2-2. A_ViewController+Category.m
#import "A_ViewController.h"
#import "A_ViewController+Category.h"
#implementation A_ViewController(Category)
-(void) categoryMethod {
NSLog(#"it's A_ViewController+Category");
}
#end
3-1. B_ViewController.h
#interface B_ViewController {
// some instance variables
}
-(void) myMethod;
3-2. B_ViewController.m
#import "B_ViewController.h"
#import "A_ViewController.h"
#interface A_ViewController() // i think it's A_ViewController extension, not A_ViewController+Category, am i right?
-(void) categoryMethod;
#end
#implementation B_ViewController
-(void) myMethod
{
A_ViewController *obj = [[A_ViewController alloc] init];
[obj categoryMethod];
}
#end
i thought it's crashed because i'm not import A_ViewController+Category.h
and i'm not implement -(void) categoryMethod in B_ViewController.m
but it works fine, and no warning.
how [obj categoryMethod] can be linked??
if that's perfectly fine syntax, i have extension question.
if i have another category called A_ViewController+Category2
here's A_ViewController+Category.h
#interface A_ViewController(Category2)
-(void) categoryMethod;
#end
and A_ViewController+Category2.m
#import "A_ViewController.h"
#import "A_ViewController+Category2.h"
#implementation A_ViewController(Category2)
-(void) categoryMethod
{
NSLog(#"it's A_ViewController+Category2");
}
#end
and this situation, if i write a code like 3-2,
then [obj categoryMethod] can' be guaranteed
that comes from A_ViewController+Category or A_ViewController+Category2, right?
I'm struggling slightly to work out what you're trying to do but in regard to standard category behaviour.
If you want to call categoryMethod on an instance of A_ViewController from within B_BiewController you need to import the header that contains the category method #interface.
You can't declare the private category () on A_ViewController from within B_ViewController.m, as the private category or class extension is a special category.
B_ViewController.m should look like this
#import "B_ViewController.h"
#import "A_ViewController+Category.h"
#implementation B_ViewController
- (void) myMethod {
A_ViewController *obj = [[A_ViewController alloc] init];
[obj categoryMethod];
}
#end
Edit
I've just noticed your Category2 method name is the same as your Category method name. This is incorrect and undefined behaviour in Objective C.
Related
I started testing ECSlidingViewController and after I tried to access FirstTopViewController I have a big trouble - because in FirstToViewController I already have ZBarReaderDelegate implemented and all examples of delegate are not triggering any method from my delegate.
Basically I have this stuff:
FirstTopViewController.h
#import ...MyStuff...
#import "UnderRightViewController.h"
#interface FirstTopViewController : UIViewController <RightViewDelegate, ZBarReaderDelegate>
#property (weak, nonatomic) IBOutlet UITextView *labelTotal;
#end
FirstTopViewController.m
#import "FirstTopViewController.h"
#implementation FirstTopViewController
- (void)setTotalViewController:(UnderRightViewController*)controller didTotalChange:(NSString*)total
{
//labelTotal.text = total;
NSLog(#"I'm here!!! and received %#", total);
}
From other side I have
UnderRightViewController.h
#import <UIKit/UIKit.h>
#import "ECSlidingViewController.h"
#class UnderRightViewController;
#protocol RightViewDelegate <NSObject>
- (void)setTotalViewController:(UnderRightViewController*)controller didTotalChange:(NSString*)total;
#end
#interface UnderRightViewController : UITableViewController
#property (nonatomic, weak) id <RightViewDelegate> delegate;
#end
UnderRightViewController.m
#import "UnderRightViewController.h"
#interface UnderRightViewController ()
#end
#implementation UnderRightViewController
#synthesize delegate;
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[delegate setTotalViewController:self didTotalChange:#"foo"];
}
#end
I'm trying this entire day solve this puzzle but I never get setTotalViewController fired.
Thanks in advance.
Friend you did a small mistake, when you navigate from FirstTopViewController to UnderRightViewController at that time you need to do this in FirstTopViewController.m:-
UnderRightViewController *obj = [[UnderRightViewController
alloc] initWithNibName:#"UnderRightViewController" bundle:nil];
obj.delegate = self; // u forget to assign protocol handler
[self.navigationController pushViewController:obj animated:YES];
[obj release];
You don't have any code that is setting the delegate for the UnderRightViewController. I don't know what object owns both of these controllers, but before either UnderRightViewController and FirstTopViewController are displayed it should run code something like this:
FirstTopViewController *ftvc = //... where ever you get a reference to this from
UnderRightViewController *urvc = ...;
urvc.delegate = ftvc;
In your above code you are using custom delegates and also you have used it for sending message to onecontroller class to another controller class. So below is the same sample code of custom delegates, it is working fine in similar way you have to implement and also the problem in your code is you are not setting the delegate, so please follow below how to set the same and call the method. here i have used your same method only return type i have defined as NSString in-spite of void for explaining purpose, but you can use void according to your requirement hope it will be helpful to you:-
First Controller Class AWindowController.h
#interface AWindowController : NSWindowController<sampleDelegate>
{
NSString *textA;
}
#property(readwrite,retain)NSString *textA;
-(IBAction)doSet:(id)sender;
#end
#import "AWindowController.h"
#import "BWindowController.h"
#interface AWindowController ()
#end
#implementation AWindowController
#synthesize textA;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (NSString *)setTotalViewController:(BWindowController*)controller didTotalChange:(NSString*)total
{
NSLog(#"recieved");
return #"recieved";
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
-(NSString*)windowNibName
{
return #"AWindowController";
}
-(IBAction)doSet:(id)sender
{
[self setTextA:#"Awindow Button Pressed"];
BWindowController *b=[[BWindowController alloc]init];
b.delegate=self;
[b showWindow:self];
}
#end
Second Controller Class BWindowController.h
#import <Cocoa/Cocoa.h>
#import "sampleDelegate.h"
#class BWindowController;
#protocol sampleDelegate <NSObject>
#required
//-(NSString *)getDataValue;
- (NSString *)setTotalViewController:(BWindowController*)controller didTotalChange:(NSString*)total;
#end
#interface BWindowController : NSWindowController<sampleDelegate>
{
NSString *bTextValue;
id<sampleDelegate>delegate;
}
#property(readwrite,retain)NSString *bTextValue;
#property(readwrite,assign)id<sampleDelegate>delegate;
#end
#import "BWindowController.h"
#interface BWindowController ()
#end
#implementation BWindowController
#synthesize bTextValue,delegate;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (NSString *)setTotalViewController:(BWindowController*)controller didTotalChange:(NSString*)total;
{
return nil;
}
- (void)windowDidLoad
{
NSString *str= [[self delegate]setTotalViewController:self didTotalChange:#"recieved"];
self.bTextValue=str;
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
-(NSString*)windowNibName
{
return #"BWindowController";
}
#end
Attached screen shot in Output:-
Below is window is the AwindowController.h class
Below in the same above window pressing the button and when Awindow button pressed data will send
and notification will be recieved in Bwindow using above define custom delegates as attached in the screen shot.
I have written two classes which contains same method (print). I want to access first class print method using second class object. How i can achieve this?
Code:
#interface classA : NSObject
-(void) print;
#end
#implementation classA
-(void) print
{
NSLog(#"hello");
}
#end
#interface classB : classA
-(void) print;
#end
#implementation classB
-(void) print{
NSLog(#"hey");
}
#end
Now i created second class object like
classB *B = [classB alloc]init];
use delegates to access other classes
#protocol
you can do like this way also
#implementation view1
(void)someMethod
{
......code of method...
}
#implementation view2
(void)fistMethod
{
view1 *abc = [[view1 alloc]init];
[abc someMethod];
[abc release];
}
also check this Objective-C call function on another class?
I have made a simple app to try a nut out the problem; this is also the first time I have used a class method so excuse me if I am doing it wrong.
ViewController.h
#import <UIKit/UIKit.h>
#import "ClassB.h"
#interface ViewController : UIViewController {
NSString *string;
}
#property (nonatomic,retain) NSString *string;
-(void)setString;
-(void)printStringViaClassB;
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize string;
- (void)viewDidLoad
{
NSLog(#"I'm inside viewDidLoad");
[super viewDidLoad];
[self setString];
[self printStringViaClassB];
}
-(void)setString {
NSLog(#"I'm inside the setString Method of the ViewController Class");
string = #"HelloWorld";
NSLog(#"String set to: %#",string);
}
-(void)printStringViaClassB {
NSLog(#"I'm inside the printStringViaClassB method of the ViewController Class");
[ClassB printLog];
}
ClassB.h
#import <Foundation/Foundation.h>
#import "ViewController.h"
#class ViewController;
#interface ClassB : NSObject{
}
+(void)printLog;
#end
ClassB.m
#import "ClassB.h"
#implementation ClassB {
}
+(void)printLog {
NSLog(#"I'm in the PrintLog Method of ClassB");
ViewController* VC = [[ViewController alloc] init];
NSLog(#"The set string is: %#",VC.string);
}
#end
This is the resulting log; as you can see, it is showing "(null)" when accessing the string from B instead of HelloWorld.
2011-11-20 14:21:18.223 ClassA[2253:f803] I'm inside viewDidLoad
2011-11-20 14:21:18.224 ClassA[2253:f803] I'm inside the setString Method of the ViewController Class
2011-11-20 14:21:18.225 ClassA[2253:f803] String set to: HelloWorld
2011-11-20 14:21:18.225 ClassA[2253:f803] I'm inside the printStringViaClassB method of the ViewController Class
2011-11-20 14:21:18.226 ClassA[2253:f803] I'm in the PrintLog Method of ClassB
2011-11-20 14:21:18.226 ClassA[2253:f803] The set string is: (null)
When you run the following code: ViewController* VC = [[ViewController alloc] init];, you are creating a NEW instance of ViewController. Since new instances of ViewController don't have their string value until their loaded (i.e., until viewdidload is run), you are simply printing out null.
Try passing in the string value you want to access from the second class as a parameter:
+(void)printLog:(NSString*)log;
+(void)printLog:(NSString*)log {
NSLog(#"I'm in the PrintLog Method of ClassB");
NSLog(#"The set string is: %#",log);
}
to call the function, say [ClassB printLog:string]; instead of [ClassB printLog];
I have been reading numerous books on iPhone development and doing the examples but I notice the idea of MVC is not really being taught correctly (although the authors do say that Xcode "lends itself" to the MVC way of coding).
A quick example. I want to make a simple calculator app (as many who are starting out do).
I have a working version with all of my code inside the xxxxxViewController.m file. Actions and Outlets all working well. The trouble with this approach is if I want to have multiple views (normal calculator and scientific calculator) I would have copy and paste my code so I now have two versions. I am clearly trying to avoid this.
So, I have created my own class (based on NSObject) as my CalculatorEngine.
Trouble is when trying to allocate and initialise my CalculatorEngine I receive errors such as "Redefinition of CalculatorEngine with a different type" and "Type specifier missing, defaults to int".
I guess I am missing something obvious.
Can you point me in the direction of a sample of any kind where a separate class is being used as an "engine" rather than having the code inside the xxxxViewController?
At this point the code below does not actually do anything. I am just trying to get the CalculatorEngine object useable in CalculatorViewController.m. This is where I receive the error.
// CalculatorAppDelegate.h
// Calculator
#import <UIKit/UIKit.h>
#class CalculatorViewController, CalculatorEngine;
#interface CalculatorAppDelegate : NSObject <UIApplicationDelegate>
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet CalculatorViewController *viewController;
#end
// CalculatorAppDelegate.m
// Calculator
#import "CalculatorAppDelegate.h"
#import "CalculatorViewController.h"
#implementation CalculatorAppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
- (void)dealloc
{
}
#end
// CalculatorViewController.h
// Calculator
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController
#end
// CalculatorViewController.m
// Calculator
#import "CalculatorViewController.h"
#import "CalculatorEngine.h"
#implementation CalculatorViewController
// This was wrong here. Now moved to viewDidLoad().
//CalculatorEngine *CalcEng;
//CalcEng = [[CalculatorEngine alloc] init];
// trouble here. CalcEng is unknow.
-(IBAction)buttonPressed:(id)sender {
[CalcEng setRegisterX:1];
}
- (void)viewDidLoad
{
CalculatorEngine *CalcEng;
CalcEng = [[CalculatorEngine alloc] init];
[super viewDidLoad]
}
- (void)didReceiveMemoryWarning
{
}
- (void)viewDidUnload
{
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
}
#end
// CalculatorEngine.h
// Calculator
#import <Foundation/Foundation.h>
#interface CalculatorEngine : NSObject
#end
// CalculatorEngine.m
// Calculator
#import "CalculatorEngine.h"
#implementation CalculatorEngine
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
#end
This code is in the wrong location:
CalculatorEngine *CalcEng;
CalcEng = [[CalculatorEngine alloc] init];
Put that into -(void)viewDidLoad.
UPDATE
You cannot call your method because your view controller is not keeping a reference to the CalcEngine (by the way, variables like this should be camel cased to keep in line with naming conventions, so it would be calcEngine). To keep a reference you need to add an iVar, or more appropriately, a property called CalcEngine. To do this, your CalculatorViewController header would look like this:
// CalculatorViewController.h
// Calculator
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController {
CalculatorEngine *CalcEngine;
}
#property (retain) CalculatorEngine *CalcEngine;
#end
Your implementation would look like this:
// CalculatorViewController.m
// Calculator
#import "CalculatorViewController.h"
#import "CalculatorEngine.h"
#implementation CalculatorViewController
// This was wrong here. Now moved to viewDidLoad().
//CalculatorEngine *CalcEng;
//CalcEng = [[CalculatorEngine alloc] init];
// trouble here. CalcEng is unknow.
-(IBAction)buttonPressed:(id)sender {
[CalcEng setRegisterX:1];
}
- (void)viewDidLoad
{
CalcEng = [[CalculatorEngine alloc] init];
[super viewDidLoad]
}
- (void)didReceiveMemoryWarning
{
}
- (void)viewDidUnload
{
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
}
#end
Don't take this the wrong way, but you should spend some time reading Apple's Objective C guide. The problems you are having have nothing to do with MVC, but with objective-c.
Did you?
#class CalculatorEngine;
But not?
#import "CalculatorEngine.h"
So I've built a few apps and am now trying my hand at building a piece of iPhone code that others can drop into their applications. Question is how do I hide the data elements in an object class header file (.h) from the user?
For example, not sure if people have used the medialets iPhone analytics but their .h does not have any data elements defined. It looks like:
#import <UIKit/UIKit.h>
#class CLLocationManager;
#class CLLocation;
#interface FlurryAPI : NSObject {
}
//miscellaneous function calls
#end
With that header file, they also supply an assembly file (.a) that has some data elements in it. How do they maintain those data elements across the life span of the object without declaring them in the .h file?
I am not sure if it matters but the .h file is only used to create a singleton object, not multiple objects of the same class (FlurryAPI).
Any help would be much appreciated. Thanks
Take a look at this:
Hide instance variable from header file in Objective C
In my header file I'd have:
#interface PublicClass : NSObject
{
}
- (void)theInt;
#end
In my source file I'd have:
#interface PrivateClass : PublicClass
{
int theInt;
}
- (id)initPrivate;
#end;
#implementation PublicClass
- (int)theInt
{
return 0; // this won't get called
}
- (id)init
{
[self release];
self = [[PrivateClass alloc] initPrivate];
return self;
}
- (id)initPrivate
{
if ((self = [super init]))
{
}
return self;
}
#end
#implementation PrivateClass
- (int)theInt
{
return theInt; // this will get called
}
- (id)initPrivate
{
if ((self = [super initPrivate]))
{
theInt = 666;
}
return self;
}
#end
I'm using theInt as an example. Add other variables to suit your taste.
I recommend you to use categories to hide methods.
.h
#import <Foundation/Foundation.h>
#interface EncapsulationObject : NSObject {
#private
int value;
NSNumber *num;
}
- (void)display;
#end
.m
#import "EncapsulationObject.h"
#interface EncapsulationObject()
#property (nonatomic) int value;
#property (nonatomic, retain) NSNumber *num;
#end
#implementation EncapsulationObject
#synthesize value;
#synthesize num;
- (id)init {
if ((self == [super init])) {
value = 0;
num = [[NSNumber alloc] initWithInt:10];
}
return self;
}
- (void)display {
NSLog(#"%d, %#", value, num);
}
- (void)dealloc {
[num release];
[super dealloc];
}
#end
You can't access to the private instance variables via dot notation, but you can still get the value by using [anObject num], though the compiler will generate a warning. This is why our apps can get rejected by Apple by calling PRIVATE APIs.