Can I replace an property with new one using some obj-c runtime features.
So I have a class A which contains a property:
#property (nonatomic, strong) Status *status;
So I want to inherit from this class like ClassB : ClassA and have ability to switch original #property (nonatomic, strong) Status *status; to my new property like #property (nonatomic, assign) NSInteger status;
So the reasone why I needed because I don't want to have a full copy of class A which contains 20 properties, so I just want to inherit from it and replace one with needed type.
Not sure if this possible, but I know something like swizzling and some obj-c runtime features can make a magic in the code.
Related
I have gone through couple of video and blog tuts online. Felt like i understood everything, however still struggling to implement the Abstract factory pattern. Here is my requirement:
I have a User class which should give a user object.
There are two types of users in my application for e.g Service Provider (Provider) and Service receiver (Consumer).
There are some common properties between these two types of users like name, email id , mobile number etc. For Provider type there will be some extra properties.
Provider types could be of for e.g. TaxiDriver or Restaurant etc.
I want to implement Abstract factory and factory method pattern for this user class so that the application can be decoupled from the User model and whenever the application wants an User of type Provider or Consumer it should get the right object.
What I tried so far:
AbstracUserProtocol.h
#protocol AbstractUserProtocol
#required
#property(nonatomic, strong) id delegate;
#property(nonatomic, readonly, getter=isExist) BOOL exist;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *emailID;
#property (nonatomic, assign) NSInteger phoneNumber;
-(void)saveUserData;
-(void)retrievUserData;
#end
AbstractUser.h
#interface AbstractUser : NSObject <AbstractUserProtocol>
-(id)initWithType:(UserType)usrType;
#end
AbstractUser.m
#implementation AbstractUser
#synthesize delegate, exist, name, emailID, phoneNumber;
-(id)initWithType:(UserType)usrType
{
self = nil;
if (usrType == kConsumer) {
self = [Consumer alloc]init];
}
else if (usrType == kProvider) {
self = [ProviderFactory alloc] initWithServiceType:TaxiService];
}
return self;
}
-(void)saveUserData {
[NSException raise:NSInternalInconsistencyException
format:#"You have not implemented %# in %#",
NSStringFromSelector(_cmd), NSStringFromClass([self class])];
}
-(void)retrievUserData {
}
#end
Now created two subclasses Consumer and ProviderFactory from AbstractUser class.
Consumer.h
#interface Consumer : AbstractUser
#property (nonatomic, strong) NSString *address;
#end
ProviderFactory.h
#interface ProviderFactory : AbstractUser
-(id)initWithServiceType:(ServiceType)srvType;
#property(nonatomic, strong) NSString *ownerDetails;
#end
So whenever in future if my application want to support another business user like Taxi and Restaurant type then I just have to create a class and init through ProviderFactory class.
Is my approach correct for the abstract Factory pattern? Appreciate any guidance.
Based on your follow up question, I edited the answer.
I'm not entirely sure you need to use AbstractFactory for what you are trying to accomplish. The basics of AbstractFactory is that it allows you to
provide an interface for creating families of related or dependent
objects without specifying their concrete classes (Gamma et al.)
Take this example from Design Patterns: Elements of Reusable Object-Oriented Software by Gamma et al. Let's say you are creating a toolkit to build user interfaces for a document editor. You may have a bunch of widget objects like scrollers, buttons, toolbars, etc. But you may want to later add a different look-and-feel to the document editor. So then you can use AbstractFactory to provide an interface to create all of the widget products (i.e. createScrollbar, createButtons, etc.) but then to change the look-and-feel, you subclass the abstract class and override the methods so that say, createScrollbar returns a scrollbar that has a 3-D effect. In another case, you subclass the abstract class to create a pink scrollbar. The options are endless, and since your client code doesn't care what the scrollbar looks like (all the client cares about is whether it scrolls text or not), you can easily add future looks-and-feels without touching the client code.
In your case, the client cares what kind of AbstractUser it is getting because in some cases it needs a Customer and in some cases a Provider. So either way, your client code would have to be changed if in the future you added a new kind of user.
That said, I think the best approach for you would be to just create an abstract base class of User and then subclass it and add the user-specific properties to the subclass. Here is an example of what I mean.
AbstractUser.h
#import <Foundation/Foundation.h>
#interface AbstractUser : NSObject
#property(nonatomic, strong) id delegate;
#property(nonatomic, readonly, getter=isExist) BOOL exist;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *emailID;
#property (nonatomic, assign) NSInteger phoneNumber;
#end
Consumer.h
#import "AbstractUser.h"
#interface Consumer : AbstractUser
#property (strong, nonatomic) NSString *address;
#end
ViewController.m
#import "ViewController.h"
#import "Consumer.h"
#import "Provider.h"
#interface ViewController ()
#property (strong, nonatomic) Consumer *consumer;
#property (strong, nonatomic) Provider *provider;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_consumer = [[Consumer alloc] init];
_provider = [[Provider alloc] init];
self.consumer.name = #"Jason";
self.consumer.address = #"some address";
self.provider.name = #"Stack Overflow";
#end
In objective-C, is it possible to do something like the following?
#property (nonatomic, strong) <UIWebView or WKWebView> *webView;
In this case I know I can use UIView or even id, but I think the scope is too broad.
Thanks!
Yes, but you would do that via class inheritance and declare the property to be of type base class, which is the super class of the view classes you want to support:
#property (nonatomic, strong) SomeBaseView *webView;
You could declare the property to be of any type (id), however that would require additional testing when actually using the instance, in order to avoid unrecognized selector exceptions:
#property (nonatomic, strong) id webView;
Perhaps the best option is to declare a protocol to confine the any type:
#property (nonatomic, strong) id<WebViewProtocol> webView;
I have two classes.
GameData.h
#import "TeamData.h"
#property (assign, nonatomic) GameData* teamA;
TeamData.h
#interface TeamData : NSObject
#property (nonatomic, copy) NSString* teamName;
-(void) printTeamData;
A number of questions :
Inside GameData.m I have this code :
TeamData* team = self.teamA;
[team printTeamData];
The first line shows this warning :
Incompatible pointer types from TeamData* with an expression of type TeamData*
In another class, I am including GameData.h and I want to set the teamA name. How do I access that? So I want to fetch the teamA property from the GameData class and set its name property.
In GameData.h, your property points to its own class, not to TeamData
#property (assign, nonatomic) GameData* teamA;
assign is meant for primitive types such as BOOL or NSInteger.
The parent class should hold a strong reference to a child object.
So your property would be better off as
#property (strong, nonatomic) TeamData* teamA;
As for setting the teamA property, you would call setTeamA: on your GameData instance:
[myGameData setTeamA:...];
I want to declare a public NSString property in my class which acts as a readonly property outside my class but i can assign any value to it inside my class. How can i achieve this behavior.
You have to declare your property in the .h file like this
#interface MyClass : NSObject
#property (strong, nonatomic, readonly) NSString *aString;
#end
but in your .m file you have to have
#interface MyClass () // your anonymous category
#property (strong, nonatomic, readwrite) NSString *aString;
#end
#implementation MyClass
#end
Externally the aString is readonly and internally you can set the value (readwrite).
You are achieving it by implementing a anonymous category also known as class extension in Objective-C
Define the property as readonly in the header and declare it readWrite in the implementation file in a class extension. The property will be readonly outside the classs implementation and read/write in the implementation.
// Interface file:
#interface Test : NSObject
#property (nonatomic, copy, readonly) NSString *propertyString;
#end
// Implementation file:
#interface Test () // Class Extension
#property (nonatomic, copy, readwrite) NSString *propertyString;
#end
#implementation Test
#end
See: Use Class Extensions to Hide Private Information
As #Amin Negm-Awad points out in an answer: the interface and class extension do not need to be in an interface or implementation file albeit this is the usual usage.
In .h file add:
#property(nonatomic,readonly)NSString* property;
In .m file add:
#interface yourClass ()
#property(nonatomic,readwrite)NSString* property;
#end
Define the property as readonly in your header file (interface), and as readwrite in your implementation file. That also allows you easily to make it weak / strong / copy.
This might be quit obvious:
in your .h file declare property as readonly
#property (nonatomic, assign, readonly, getter = isLoading) BOOL loading;
in your .m file declare property as readwrite
#property (nonatomic, assign, readwrite, getter = isLoading) BOOL loading;
This is an example, obviously you should create strong NSString property, and I assume compiler won't allow to set other value outside the class, but inside it will.
Beside the existing answers that told you to define a readonly property and change it to a readwrite property, which is completely correct and the intended pattern (that is, what readwrite is for), I want to add an probably important information:
You put the readonly definition in the interface. (Not header!)
You put the readwrite definition in a class continuation. (Not implementation file)
One might say that this is the same, because interfaces reside in the header and class continuations reside in the implementation file. But this is only the usual case.
You can additionally put class continuations in a third file. Then something like a "friend class" can import it additionally and this "fried class" has write access. I do that very often, when developing frameworks.
MyClass.h: // public header, available for everybody
#interface MyClass : NSObject
#property (readonly, …) id property1; // Everyone can read it
#property (readonly, …) id property2; // Everyone can read it
- (void)method; // Everyone can use it
#end
MyClass_Package.h: // project header, available for framework classes, unavailable for the user of the framework
#interface MyClass()
#property (readwrite, …) id property1; // All classes inside the framework can write it
- (void)packageMethod; // All classes inside the framework can use it
#end
MyClass.m
#interface MyClass() // A second class extension inside .m
#property (readwrite, …) id property2; // Only MyClass can write it
- (void)privateMethod; // Only MyClass can use it
#end
Define the property as readonly in the header, and set it using the underscore syntax.
#property (nonatomic, readonly) NSString *myString;
- (void)someMethodInYourDotMFile {
_myString = YES;
}
I'm new to objective-C, so apologies if this is repeated somewhere. I have a category(?) that is something like:
inside SomeClass.h:
#interface SomeClass (SomeCategory) <SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#property (nonatomic, retain) id someProperty; // <-- i want to move this to "private"
#end
and now in my SomeClass.m, all i have is:
#implementation SomeClass (SomeCategory)
// dynamic setters/getters here for someProperty.
#end
I think the someProperty is public. how do i make this "private"? (in other words, how do i syntactically put this in the .m file? i tried to use
#interface SomeClass (SomeCategory) {
#property (nonatomic, retain) somePrivateProperty;
}
#end
but it just complains that i have duplicate definition of the category. how do i do this correctly?
In your .h file, you should not give the category. Just use:
#interface SomeClass : SomeBaseClass < SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#end
In your .m file, define your private property inside a class extension:
#interface SomeClass ()
#property (nonatomic, retain) id somePrivateProperty;
#end
A class extension is not a like category in that it allows you to extend an interface as well as add new storage to your class.
In a class category, you can define new properties, but no storage will be allocated for it, so you have to do it by hand:
#interface SomeClass (SomeBaseCategory)
#property (nonatomic, retain) id somePrivateProperty;
#end
#implementation SomeClass {
id _somePrivateProperty;
}
- (void)setSomePrivateProperty:(id)property {
_somePrivateProperty = property;
}
- (id)somePrivateProperty {
return _somePrivateProperty;
}
#end
Otherwise your app will crash.
In any case, keep in mind that given the dynamic nature of Objective-C, your property will never be fully private, since you can always send a message to an Objective-C object through objc_msgsend and thus set or read the property value.
EDIT:
If you do not have the source code for a class implementation, you cannot define a class extension (as per source linked above).
In this case, you could use object association to define properties.
Just add the category definition in the .m file OUTSIDE the implementation block
Like so:
#interface MyClass (MyCategory)
#property (assign) BOOL myPrivateProperty;
#end
#implementation MyClass
...
#end
Categories are best used for adding capability to code you do not own and cannot change. Adding properties via categories is not impossible, but is much more difficult.
Class Extensions are best used for keeping properties your object needs, but are not intended to be public.
If you do truly need to add properties to this object, the way to do it is with the Objective-C runtime's associated objects
There's an excellent writeup of when/how to use them here