Is there any Objective C magic I can use to do the following:
Say I have a Class Foo, with two properties myObj A and otherObj B.
Now...
In the Class implementation file (.m) of myObj (A) I want to access
it's "sibling" property "otherObj B" in the "parent" Class Foo.
Is there something similar to this in objective C to help access sibling properties:
self.ParentClass.b; ?
or
self.OwningClass.b; ?
I know convention is probably to pass a refence to B during init of A, but I don't want to have to create many custom initalisers
Just wondered if there was a shortcut.
#interface Foo : NSObject
#property A
#property B
#end
#interface A : NSObject
#property B
#end
#implementation A : NSObject
self.B = self.parent.B;
#end
Thanks,
If you have
#interface A : NSObject
#property A
#end
#interface B : NSObject
#property B
#end
then A has no way to know B and vice versa. As you said: you have to pass a reference
IF you ever only have ONE instance of A and ONE instance of B then you COULD use a singleton pattern. But it introduces extra dependencies and makes the code less reusable. so while it might work, thats not a way I would recommend.
There's no built in "parent" or "owner" concept (large because the object could, theoretically, have strong references to it maintained from multiple objects). But just add your own parent property:
Thus, consider:
#interface Foo : NSObject
#property (nonatomic, strong) A *a;
#property (nonatomic, strong) B *b;
#end
#interface A : NSObject
#property (nonatomic, weak) Foo *parent;
- (id)initWithParent:(Foo *)foo;
#end
#interface B : NSObject
#property (nonatomic, weak) Foo *parent;
- (id)initWithParent:(Foo *)foo;
#end
So, Foo will own instances of A and B objects (thus the strong references), and A and B will maintain a weak reference to their parent. Note, the weak reference to the parent is critical, to avoid strong reference cycles (a.k.a. retain cycles).
The implementations of these classes might look like:
#implementation Foo
- (id)init
{
self = [super init];
if (self) {
_a = [[A alloc] initWithParent:self];
_b = [[B alloc] initWithParent:self];
}
return self;
}
#end
#implementation A
- (id)initWithParent:(Foo *)foo
{
self = [super init];
if (self) {
_parent = foo;
}
return self;
}
- (void)someRandomMethod
{
B *sibling = self.parent.b;
// now do whatever you want with your reference to the sibling object
}
#end
#implementation B
- (id)initWithParent:(Foo *)foo
{
self = [super init];
if (self) {
_parent = foo;
}
return self;
}
#end
Notice, in A I have a someRandomMethod which illustrates how it could get access to its sibling, B. You could do the same thing from B to A, too.
I know you say you don't want to have to write your own custom init methods, but that's the right way to do it. Or just call the default init method and manually set the parent property of the child objects, e.g.:
_a = [[A alloc] init];
_a.parent = self;
_b = [[B alloc] init];
_b.parent = self;
Related
I'm building a Static Library for iOS and I'd like to have some methods accessible to all the classes in the library, but not outside the library. Let's make an example:
This is a class called A with two methods available outside the library:
#interface A : NSObject
-(void)methoAvailableOutside1;
-(void)methoAvailableOutside2;
//This method has to be visible only to classes within the library
-(void)methodInternalToTheLibrary;
#end
The class called B is still internal to the library. It can call all methods belonging to A (also the method that should be "internal"):
#import "A.h"
#interface B : NSObject
#property A* aObject;
#end
This is the implementation of B:
#import "B.h"
#implementation B
-(instancetype)init{
self = [super init];
if(self){
_aObject = [[A alloc]init];
[_aObject methoAvailableOutside1];
[_aObject methoAvailableOutside2];
//here I can call the "internal" method
[_aObject methodInternalToTheLibrary];
}
return self;
}
#end
Now let's write an EXTERNAL class (external to the library, clearly):
#import "MyCustomLibrary.h"
#interface ExternalClass : NSObject
#property A* aObject;
#end
This is the implementation of the external class:
#import "ExternalClass.h"
#implementation ExternalClass
- (instancetype)init
{
self = [super init];
if (self) {
_aObject = [[A alloc]init];
[_aObject methoAvailableOutside1];
[_aObject methoAvailableOutside2];
//!!!Here THIS SHOULD BE...
[_aObject methodInternalToTheLibrary];
//...FORBIDDEN!!!
}
return self;
}
#end
How can I achieve this? Thank you in advance.
The only way I can think of is to have an additional header file with the additional method defined in that header. Anonymous categories.
publicHeader.h
#interface A : NSObject
-(void)methoAvailableOutside1;
-(void)methoAvailableOutside2;
#end
And then a mother .h file only used inside your library code.
privateHeader.h
#interface A()
//This method has to be visible only to classes within the library
-(void)methodInternalToTheLibrary;
#end
Could that work? It won't guarantee that other code can't call the method but the intention would be clear.
I have two classes:
Base class is player it has property name
Sub class is computer
How can I set the name of the computer player by accessing the base class property?
base class
#interface Player : NSObject
#property (nonatomic) NSString* playerName;
#property (nonatomic) int score;
-(id) initWithName :(NSString*) playerName;
-(void) addScore:(int) points;
#end
subclass
#interface Computer : Player
#end
.m
#import "Computer.h"
#implementation Computer
-(id)initWithName:(NSString *)name{
self = [super init];
}
#end
Your question is difficult to make out so please let me know if I've answered something other than what you are asking.
Assuming you have a class, car, that has inherited from a super class, vehicle, and that vehicle has an attribute, wheels, you can get at that in this way:
super.wheels = 4;
Call the property in the Computer Class like
super.name = #"Bob";
You can also call the methods of the superClass with
super.myMethod();
You can access the property from the superclass in the subclass directly.
Player.h
#interface Player : NSObject
#property(nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
#end
Computer.h
#interface Computer : Player
#end
Computer.m
#implementation Computer
- (instancetype)initWithName:(NSString *)name {
self = [super initWithName:name];
if (self) {
self.name = #"test";
}
return self;
}
#end
Although, as #Phillip Mills suggested, it's probably better practice to set the name property in Player.m instead.
I am practicing inheritance in Objective-C and this is my Person parent class
// This is Person.h
#interface Person : NSObject
#property(nonatomic, strong) NSNumber *age;
#property(nonatomic, strong) NSString *race;
-(instancetype)init;
-(instancetype)initWithAge:(NSNumber*)age andRace:(NSString*)race;
#end
This is what I'm trying to do in my Student class
// This is Student.h
#import "Person.h"
#interface Student : Person
#property(nonatomic, strong) NSString *classification;
#property(nonatomic, strong) NSString *major;
#end
And
// This is Student.m
#import "Student.h"
#import "Person.h"
#implementation Student
-(instancetype)init
{
return [self initWithClassification:#"Freshman" andMajor:#"Computer Science"
andAge:[[NSNumber alloc] initWithInt:20] andRace:#"Caucasian"];
}
-(instancetype)initWithClassification:(NSString*)classification andMajor:(NSString*)major
andAge:(NSNumber*)age andRace:(NSString*)race
{
self = [super init];
if (self)
{
_classification = classification;
_major = major;
_age = age;
_race = race;
}
return self;
}
#end
The compiler is not liking my doing
_age = age;
_race = race;
Use of undeclared identifier _age did you mean age? Can someone tell me where I went wrong? Thank you.
When you declare a property like that, clang will automatically #synthesize it for you (i.e it will create a getter and setter), but synthesized properties are not visible to subclasses, you have different alternatives to make it working.
You can synthesize the ivar in the interface of the subclass
#synthesize age = _age;
Or you can declare the ivar protected on the interface of the superclass, so that will be visible on the subclasses.
#interface Person : NSObject {
#protected NSNumber *_age;
}
Or you can use self.age = ... on your subclass, without using the ivar at all.
Since clang compiler now auto-synthesise properties you don't have, in most cases, to synthesise your properties.
Objective-C Autosynthesis of Properties
Clang provides support for autosynthesis of declared properties. Using
this feature, clang provides default synthesis of those properties not
declared #dynamic and not having user provided backing getter and
setter methods. __has_feature(objc_default_synthesize_properties)
checks for availability of this feature in version of clang being
used.
But in some cases (some examples are in this question) you should explicitly synthesise them.
In this case, to solve your problems you should just add:
#synthesize age = _age;
#synthesize race = _race;
to your code, and you'll be fine.
The subclass has access to the property, but not the backing variable. So you should set it with
self.age = age;
I have four classes MainVC, ParentClient and ChildClient1, ChildClient2(which are subclasses of ParentClient). ParentClient has a delegate to MainVC such that in MainVC
- (void)viewDidLoad
{
[super viewDidLoad];
[ParentClient instance].mainViewDelegate = self;
}
And then the ParentClient looks like this
#interface BaseClient : NSObject
#property (assign) id<MainVCInteraction> mainViewDelegate;
+(instancetype) instance;
#end
Now I want to access mainViewDelegate from ChildClient1, ChildClient2 and it returns me nil while [ParentClient instance].mainViewDelegate returns the correct value
Here is what I did I removed the BaseClient Class so that ChildClient1, ChildClient2 were no longer subclasses of BaseClient. I defined a objective-c protocol file MainVCInteaction.h and made Client1, Client2 look like this:
#import "MainVCInteraction.h"
#interface ChildClient1 : NSObject
#property (assign) id<MainVCInteraction> mainViewDelegate;
+(instancetype) instance;
#end
#import "MainVCInteraction.h"
#interface ChildClient2 : NSObject
#property (assign) id<MainVCInteraction> mainViewDelegate;
+(instancetype) instance;
#end
And then MainVC implements this protocol, I assigned the delegate like this
- (void)viewDidLoad
{
[super viewDidLoad];
[ChildClient1 instance].mainViewDelegate = self;
[ChildClient2 instance].mainViewDelegate = self;
}
I have Parent/child class like this:
#interface Parent : MTLModel <MTLJSONSerializing>
- (void)someMethod;
#property a,b,c...; // from the JSON
#property NSArray *childs; // from the JSON
#end
#interface Child : MTLModel <MTLJSONSerializing>
#property d,e,f,...; // from the JSON
#property Parent *parent; // *not* in the JSON
#end
All the fields a to f are in the JSON, with the same name (hence my JSONKeyPathsByPropertyKey method return nil), and the proper JSONTransformer is correctly setup so that the childs array in parent is containing Child class and not NSDictionary.
Everything work forwardly.
But I want, as a convenience, a property in my Child model that reference back to the parent that own it. So that in the code I can do that:
[childInstance.parent someMethod]
How do I do that with Mantle ??
I want, when the parent is parsing the child's JSON and creating the Child class, to add a ref to itself. (With an init method ??)
Thanks.
I do this by overriding MTLModel -initWithDictionary:error: method. Something like this.
Child interface:
#interface BRPerson : MTLModel <MTLJSONSerializing>
#property (nonatomic, copy, readonly) NSString *name;
#property (strong, nonatomic) BRGroup *group; // parent
#end
In parent implementation:
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error {
self = [super initWithDictionary:dictionaryValue error:error];
if (self == nil) return nil;
// iterates through each child and set its parent
for (BRPerson *person in self.people) {
person.group = self;
}
return self;
}
Techinical note:
If you are curious like me, I already tried to tweak MTLJSONAdapter by changing its forwardBlock and reversibleBlock. But I can't, because those are inside MTLReversibleValueTransformer superclass, and that class is declared privately in "MTLValueTransformer.m". So the initWithDictionary approach above should be much easier.