I found this related question: How do I use composition with inheritance?
I would like to do the same with Objective-C, that is to say that a GenericView knows that its property obj is a GenericObject, and that a SpecializedView knows that the very same obj property is a SpecializedObject.
Here is an example that will be clearer:
//
// Example.m
#import <UIKit/UIKit.h>
/* HEADER */
// Electrical Machine
#interface ElectricalMachine : NSObject {
}
- (void)plugIn;
#end
// Toaster
#interface Toaster : ElectricalMachine {
}
- (float)getThermostat;
#end
// GenericView
#interface GenericView : NSObject {
ElectricalMachine *machine;
}
- (void)doSomethingGeneric;
#property (nonatomic, retain) ElectricalMachine *machine;
#end
//SpecializedView
#interface SpecializedView : GenericView {
}
- (void)doSomethingSpecialized;
#end
/* IMPLEMENTATION */
// GenericView
#implementation GenericView
#synthesize machine;
- (void)doSomethingGeneric {
Toaster *toaster = [[Toaster alloc] init];
[toaster plugIn];
self.machine = toaster;
[toaster release];
}
#end
// SpecializedView
#implementation SpecializedView
- (void)doSomethingSpecialized {
/* ERROR HERE
* Incompatible types in initialization
* 'ElectricalMachine' may not respond to '-getThermostat'
*/
float r = [machine getThermostat];
r = r;
// ...
}
#end
As you see, I get an error at the end, because for SpecializedView the machine property is an ElectricalMachine, not a Toaster.
Thank you very much for your help!
Old Question
Here is the first version of my question, which was maybe too cryptic:
I have the following generic view:
#interface GenericView {
GenericObject obj;
}
- (id)doSomething;
I also have the following specialized view:
#interface SpecializedView : GenericView {
}
- (id)doSomethingElse;
I have the following object:
#interface GenericObject {
}
- (id)plugIn;
and the following specialized object:
#interface SpecializedObject : GenericObject {
}
- (float)toastTime;
Let's say I want GenericView to handle GenericObject, and SpecializedView to handle the same object, knowing that it is SpecializedObject.
Let me explain by showing implementations:
GenericView doSomething
- (id)doSomething {
[obj plugIn];
}
SpecializedView doSomethingElse
- (id)doSomethingElse {
// ERROR here
float time = [obj toastTime];
}
I will get the following warning:
'GenericObject' may not respond to '-toastBread'
and the following error:
Incompatible types in assignement
Which is logical, since I have defined the type of obj as GenericObject. I want to be able to use methods from GenericObject in GenericView, and methods from SpecializedObject in SpecializedView. Is there a way to precise that obj has to be a GenericObject in GenericView to be handled, and has to be a SpecializedObject to be dealt with in SpecializedView, without adding a property? How would you do that?
Objective-C is a dynamically-typed language and methods are resolved at runtime, not compile time. If in SpecializedView, obj is in fact of an object of type SpecializedObject (even though it's declared as GenericObject), it will in fact respond to a toastBread message. The compiler will generate a warning but you can ignore it.
If SpecializedView may have both GenericObjects and SpecializedObjects, you can make sure that obj responds to toastBread using the respondsToSelector: message (inherited from NSObject):
if ([obj respondsToSelector:#selector(toastBread)]) {
[obj toastBread];
}
Related
Consider the following code which tries to work with the non-NSObject class object __NSMessageBuilder:
- (Class)getTestClass {
Class class = objc_getClass("__NSMessageBuilder");
return class;
}
- (void)testNonNSObjectClass {
// case 1
Class class1 = objc_getClass("__NSMessageBuilder");
const char *name1 = class_getName(class1);
// case 2
Class class2 = [self getTestClass];
const char *name2 = class_getName(class2);
}
The first case works as expected and name1 contains the correct name of the class. However, the execution crashes on [self getTestClass] in case 2 with the following seemingly unrelated log output:
NSForwarding: warning: object 0x7fff76f18dc0 of class '__NSMessageBuilder' does not
implement doesNotRecognizeSelector: -- abort
Message from debugger: Terminated due to signal 9
This does not make any sense to me. What is going on here, and how can I fix it? The problem is not limited to __NSMessageBuilder, this also happens with other special classes like __ARCLite__ etc.
Most probably because you are using ARC. The compiler is adding retain/release messages to your code, and because your root class does not inherit from NSObject, the retain and release methods have not been implemented you get the error "class '__NSMessageBuilder' does not implement doesNotRecognizeSelector: -- abort".
I just made a test with the following code:
#import <Foundation/Foundation.h>
// RootClass.h
NS_ROOT_CLASS
#interface RootClass
#end
// RootClass.m
#import "RootClass.h"
#implementation RootClass
#end
// AppDelegate.m
#import "RootClass.h"
#import <objc/runtime.h>
#interface AppDelegate ()
#property (retain) IBOutlet NSWindow *window; // retain is important here because you will not use ARC in one of the tests
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self testNonNSObjectClass];
}
- (Class)getTestClass {
Class class = objc_getClass("RootClass");
return class;
}
- (void)testNonNSObjectClass {
// case 1
Class class1 = objc_getClass("RootClass");
const char *name1 = class_getName(class1);
// case 2
Class class2 = [self getTestClass];
const char *name2 = class_getName(class2);
}
If I run it with ARC it bombs like you found, but if you run this code without ARC (go to build phases and add the compiler flag -fno-objc-arc to AppDelegate.m) and you will not bomb.
// Marketplace.h
#import <Foundation/Foundation.h>
#import "Item.h"
#interface Marketplace : NSObject
+ (void)addItemToMarketplace:(Item *)newItem; // METHOD IN QUESTION
#end
// Marketplace.m
#import "Marketplace.h"
#interface Marketplace()
#property (strong, nonatomic) NSMutableArray *listOfItems;
#end
#implementation Marketplace
+ (void)addItemToMarketplace:(Item *)newItem // METHOD IN QUESTION
{
[self.listOfItems addObject:newItem]; // Raises 3 errors
}
#end
I have declared a class method addItemToMarketplace that takes in an object of type Item and adds this Item to the listOfItems property that I have declared in the interface of the implementation file (I am not sure that I want other classes to fiddle with this property). I have used this method in another class as such [Marketplace addItemToMarketplace:newItem]. I am not sure how to handle the three errors that are raised when I write [self.listOfItems addObject:newItem].
The 3 errors are as follows:
1. Member reference type 'struct objc_class *' is a pointer; maybe you meant to use '->'?
2. Definition of 'struct objc_class' must be imported from module 'ObjectiveC.runtime' before it is required
3. No member named 'listOfItems' in 'struct objc_class'
Making the change proposed in #1, which changes self.listOfItems to self->listOfItems, raises the error "Member reference base type 'Class' is not a structure or union"
Any help would be appreciated.
// NEW CHANGES!
After making some changes and following some suggestions I found on other sites, here's what I have so far:
// Marketplace.h
#import <Foundation/Foundation.h>
#import "Item.h"
#interface Marketplace : NSObject {}
+ (Marketplace *)sharedMarket;
- (void)addItemToMarketplace:(Item *)newItem;
#end
// Marketplace.m
#import "Marketplace.h"
#interface Marketplace()
#property (strong, nonatomic)NSMutableArray *listOfItems;
#end
static Marketplace *sharedMarketplace = nil;
#implementation Marketplace
+ (Marketplace *)sharedMarket
{
if (sharedMarketplace == nil) {
sharedMarketplace = [[super allocWithZone:NULL] init];
}
return sharedMarketplace;
}
- (void)addItemToMarketplace:(Item *)newItem
{
[self.listOfItems addObject:newItem];
}
- (id)init
{
if ( (self = [super init]) ) {
}
return self;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedMarket];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#end
Questions, comments, concerns?
Only the instance methods can operate on instance data. So you need either an instance or change the data to static too:
#interface Marketplace()
#end
static NSMutableArray *listOfItems; // static is implied, so not really necessary
#implementation Marketplace
+ (void)addItemToMarketplace:(Item *)newItem {
if (!listOfItems) listOfItems = [NSMutableArray array];
[listOfItems addObject:newItem];
}
#end
Note that there's no thread safety here.
The + prefix defines a class method. Within that method self refers to the class itself which is almost certainly not what you intend. The class does not have a listOfItems as that is a property available on each instance of the class.
It is not clear what you are attempting to do here. If you are coming from a C/C++ background perhaps you have assumed a different behavior for this method. Are you trying to add an item to a list managed by a particular instance of this class or to a list shared across all instances?
If you just have one instance of marketplace in your app consider using a singleton..
You then might add items to the single marketplace like this:
[[Marketplace theMarketplace] addItem:<*item*>]
Here's one way to set this up:
#interface Marketplace : NSObject
#property ( nonatomic ) NSArray * items ;
#end
#implementation Marketplace
static Marketplace * __marketplace ;
+(void)load
{
__marketplace = [ Marketplace new ] ;
}
+(instancetype)theMarketplace
{
return __marketplace ;
}
-(void)addItem:(Item*)item
{
self.items = [ ( self.items ?: #[] ) arrayByAddingObject:item ] ;
}
#end
(or in Swift)
class Marketplace
{
struct Static
{
static let marketplace = Marketplace()
}
var items:Array<Item> = [] ;
class func get() -> Marketplace { return Static.marketplace }
func addItem( item: Item ) { self.items += item }
}
I am getting an error in the following code. I could not guess what mistake i have done for such error to come up.
remote.h
struct RMH_REQUEST_SESSION_MSG //Message Data
{
int uDeviceID;
int uProtocolVersion;
int uReserved[5];
};
RS232MsgRequestSession.m
#implementation RS232MsgRequestSession
-(id)init
{
if (self = [super init]) {
struct RMH_REQUEST_SESSION_MSG st;
}
return self;
}
#end
xyz.h
#import "RS232MsgRequestSession.h"
#implementation xyz
-(void)Open{
RS232MsgRequestSession* pMsg = [[RS232MsgRequestSession alloc]init];
pMsg->st.uProtocolVersion = RS232_VERSION; //error
pMsg->st.uDeviceID = RS232_PROTOCOL_DEVICE_ID; //error
memset(pMsg->st.uReserved,0x00,sizeof(pMsg->st.uReserved)); //error
}
#end
Error:'struct RS232MsgRequestSession' has no member named 'st'
Put iVar declaration in class's #interface block. In your code st is just a local variable in init method:
#interface RS232MsgRequestSession : NSObject{
...
#public
struct RMH_REQUEST_SESSION_MSG st;
}
...
#end
You are defining st as a local variable in the init function.
Move struct RMH_REQUEST_SESSION_MSG st; to the class definition in RS232MsgRequestSession.h
What you forgot to do was include the struct in your #interface as a public instance variable:
#interface RMH2532MsgRequestSession : NSObject {
#public
struct RMH_REQUEST_SESSION_MSG st;
}
- (void) Open;
#end
And your #implementation should be empty:
#implementation RS232MsgRequestSession
#end
PS - The reason why you must use #public here is because in xyz, you were doing a direct dereference of the object using the member operator ->. This is not allowed by default when working with Objective-C objects. However, if the instance variables are under a #public attribute, then you will not have any problems doing this.
As you might have guessed by now, using #public is a Very Bad Idea -- usually. It breaks encapsulation and opens a pandora's box of potential problems that you wouldn't have otherwise had if you had used proper encapsulation.
I'm fairly new to Objective-C and wondering if it's possible to type objects as their supertype without receiving compiler warnings when assigning them, or if there is a recognised way of achieving the same thing?
I realise that this is what type id is for but I have a base class with synthesized properties and if I try to use id I get a build error "request for member 'x' in something not a structure or union", presumably because dynamic typing is fine for sending messages to an object but not for accessing synthesized properties.
For example in Java I might have:
public abstract class A {
public function doSomething() {
//some func
}
}
public class B extends A {
public function doSomething() {
//override some func
}
}
public class C extends A {
public function doSomething() {
//override some func
}
}
//and in my main class:
A objB = new B();
A objC = new C();
//the purpose of all of this is so I can then do:
A objHolder;
objHolder = objB;
objHolder.doSomething();
objHolder = objC;
objHolder.doSomething();
I currently have the above working in Objective-C but with a compiler warning: "assignment from distinct Objective-C type"
OK, here is the Objective-C interfaces, I can add the implementations if you want. It's a composite pattern:
//AbstractLeafNode
#import <Foundation/Foundation.h>
#interface AbstractLeafNode : NSObject {
NSString* title;
AbstractLeafNode* parent;
}
#property (nonatomic, retain) NSString* title;
#property (nonatomic, retain) AbstractLeafNode* parent;
#end
//Page
#import "AbstractLeafNode.h"
#interface Page : AbstractLeafNode {
//there will be stuff here later!
}
#end
//Menu
#import "AbstractLeafNode.h"
#interface Menu : AbstractLeafNode {
NSMutableArray* aChildren;
}
- (void)addChild:(AbstractLeafNode *)node;
- (void)removeChild:(AbstractLeafNode *)node;
- (AbstractLeafNode *)getChildAtIndex:(NSUInteger)index;
- (AbstractLeafNode *)getLastChild;
- (NSMutableArray *)getTitles;
#end
// I'd then like to do something like (It works with a warning):
AbstractLeafNode* node;
Menu* menu = [[Menu alloc] init];
Page* page = [[Page alloc] init];
node = menu;
[node someMethod];
node = page;
[node someMethod];
// Because of the synthesized properties I can't do this:
id node;
// I can do this, but I suspect that if I wanted synthesized properties on the page or menu it would fail:
node = (AbstractLeafNode*)menu;
node = (AbstractLeadNode*)page;
Sorry, as I was editing the question I realised that I was trying to do it the wrong way round and assign an AbstractLeafNode to a Menu, so the compiler warning completely makes sense. No errors when assigning a Menu to an AbstractLeafNode.
I've been staring at this for too long!
can u please help me how to return an instance of a function which is inherited through interface in Objective-C Language?
#protocol prot1
{
public IDispManager getDispManager;
}
#end
#interface A: NSObject (prot1)
{
}
#end
#implementation A
{
/**
* Provides access to the disp manager.
* #return Instance of the disp manager.
*/
public IDispManager getDispManager;
// how to return an instance of this method
}
#end
Plssss help me out???????
The class would have to hold an object of that class and provide a method to return it. Here's an example along the lines of what you wanted to write:
#protocol Proto
- (DisplayManager *)displayManager;
#end
#interface Foo : NSObject <Proto> {
DisplayManager *displayManager;
}
- (DisplayManager *)displayManager;
#end
#implementation Foo
- (DisplayManager *)displayManager {
return [[displayManager retain] autorelease];
}
#end
Though this probably won't make sense to you without understanding the language more fully.