I am working on a Swift project with custom classes. The old Objective-C version of the project had a custom init method shown below.
Custom init from custom class
-(id) initWithURLDictionary: (NSDictionary *) dictionary{
self = [super init];
if (self) {
self.URLDictionary = dictionary;
}
return self;
}
When using this class I would create the object with the custom initializer and then set the delegate for the custom class to self.
// Create a temporary dictionary of the feeds, allocate and initialize the FLODataHandler object and set the delegate (me) to self.
NSDictionary *URLTempDictionary = [[NSDictionary alloc] initWithObjectsAndKeys: kFLOCyclingURL, #"FLO Cycling", kTriathleteURL, #"Triathlete", kVeloNewsURL, #"Velo News", kCyclingNewsURL, #"Cycling News", kRoadBikeActionURL, #"Road Bike Action", kIronmanURL, #"Ironman", nil];
self.dataHandler = [[FLODataHandler alloc] initWithURLDictionary: URLTempDictionary];
self.dataHandler.delegate = self;
In Swift I am a bit confused. It seems I have two options. Option 1 would have me creating a custom initializer in the custom class.
Custom Initializer in custom class
init(dictionary : [String:String]) { self.URLDictionary = dictionary }
The process is the same as in Objective-C.
let URLTempDictionary = [kFLOCyclingURL : "FLO Cycling", kTriathleteURL : "Triathlete", kVeloNewsURL : "Velo News", kCyclingNewsURL : "Cycling News", kRoadBikeActionURL : "Road Bike Action", kIronmanURL : "Ironman"]
var tempDataHandler = FLODataHandler(dictionary: URLTempDictionary)
self.dataHandler! = tempDataHandler
Option 2 does not through a complaint but seems incomplete.
Instead of creating a custom initializer I would simply do the following. The custom class has a dictionary property called URLDictionary.
let URLTempDictionary = [kFLOCyclingURL : "FLO Cycling", kTriathleteURL : "Triathlete", kVeloNewsURL : "Velo News", kCyclingNewsURL : "Cycling News", kRoadBikeActionURL : "Road Bike Action", kIronmanURL : "Ironman"]
self.dataHandler!.URLDictionary = URLTempDictionary
self.dataHandler.delegate = self
So my question has to do with the need for the custom initializer and the use of
var tempDataHandler = FLODataHandler(dictionary: URLTempDictionary)
Does the use of
self.dataHandler!.URLDictionary = URLTempDictionary
accomplish the same result?
Take care,
Jon
The purpose of an initializer is effectively to compel the caller to supply the data - and Swift helps you by enforcing this contract in a way that Objective-C does not. If you declare init(dictionary:), all other inherited initializers cease to be inherited, and init(dictionary:) becomes the only way to make a FLODataHandler.
So if it is crucial that a FLODataHandler have a URLDictionary value from the get-go, by all means declare the initializer. Indeed, if it is crucial that it also have a delegate, declare init(dictionary:delegate:) instead. That is "best practice".
On the other hand, there is nothing inherently evil about two-stage initialization, i.e. first make the object, then give its properties values; and there are situations in real-life iOS programming where there's no real alternative (prepareForSegue comes to mind). The problem with it is that it relies on an unenforceable contract, which the caller must simply know in some other way and adhere to voluntarily.
EDIT: You also seem to be asking whether merely saying
self.dataHandler!.URLDictionary = URLTempDictionary
somehow magically creates a FLODataHandler to occupy the dataHandler property. It most certainly does not. No objects come magically into existence in Swift, any more than in Objective-C. If no one has ever said FLODataHandler(...), then no such instance exists. If no one has ever assigned a FLODataHandler instance to self.dataHandler, then there is no FLODataHandler there (and the above code will crash if it means you are trying to unwrap nil).
Related
I have an object that is set as the delegate of another object, whose delegate property is weak.
- (YYService *)service
{
XXHandler *handler = [[XXHandler alloc] init];
// YYService's "delegate" property is weak
return [[YYService alloc] initWithDelegate:handler];
// The XXHandler is deallocated because there are no strong references to it
}
Since nothing else references the delegate it ends up getting deallocated, but I want it to live for as long as the parent object does as if the parent had a strong reference to its delegate. Is there a simple way to accomplish this?
The easy why to "solve" that problem is to subclass YYService, giving the subclass an additional strong property and set that one in -initWithDelegate:.
But this "solution" would deepen a problem in your design instead of solving that.
Let's have a look, why delegates are usually hold weakly:
The delegating class has a general – or no – behavior which might not fit in the class' user's case, i. e. if something happens. (An operation completes, an error occurs, $whatever) So the delegating class gives you the opportunity to customize the behavior including running custom code. Delegating is in competition with subclassing, but in difference to subclassing is on a per instance basis (instead of a per class basis) and at run time (instead of compile time).
Because it works on per instance basis, the instance creating the delegate typically holds the delegating instance strongly. This code knows the customization that should apply to the delegating instance:
-(void)createDelegate
{
self.delegating = [Delegating new]; // I create and hold the instance strongly
delegating.delegate = self; // I customize it
}
Then the delegating instance cannot hold the delegate strongly, because this would be a retain cycle.
In your snippet that does not work, because -service returns the newly created delegating instance. And even it would be possible to return both instances, I wouldn't like it, because creating the delegating object and installing the delegate would be a two-step operation, even it is semantically a one-stepper. So If you do not have self as the delegate, you should do the whole installation process in one method:
-(void)installService
{
self.handler = [[XXHandler alloc] init]; // Hold the handler strongly
self.service = [[YYService alloc] initWithDelegate:handler];
}
If you do not know the concrete instance object acting as delegate, pass it as argument:
-(void)installServiceWithDelegate:(id)delegate
{
self.delegate = delegate;
self.service = [[YYService alloc] initWithDelegate:delegate];
}
…
[self installServiceWithDelegate:[YourConcreteClass new]];
But you should not try to turn things upside down or inside out.
I am tring to understand convenience methods.
IF I have a sqlite database containing store details and am returning these store details in a FMResultSet. I am thinking that to create an array of these store details as Store objects, that the best way would be create an object of type Store in one go in a convenience method and add to array.
The Class I have created is as below with convenience method
#interface StoreDetails : NSObject
#property (nonatomic, strong) NSString *storeName;
etc etc etc
+ (instancetype)storeWithStoreName:(NSString *)storeName
TelephoneNumber:(NSString *)
telephoneNumber: etc .......
My ResultSet loop would be as below?
NSMutableArray *Stores = [[NSMutableArray alloc] init];
while ([rs next]) {
Store *store =
[Store storeDetailsWithStoreName:[rs stringForColumn:#"storename"]
telephoneNumber:[rs stringForColumn:#"TelephoneNo"]];
[Stores addObject:store];
}
Is my thinking correct as above is is it better to go as below.
NSMutableArray *Stores = [[NSMutableArray alloc] init];
while ([rs next]) {
Store *store = [Store alloc] init];
store.storeName = [rs stringForColumn:#"storename"];
store.telephoneNumber = [rs stringForColumn:#"TelephoneNo"];
[Stores addObject:store];
}
All I am trying trying to understand is why you would use one over the other in noob speak, thankyou.
I think you have a good approach: initializing your Store object in a method of the Store class.
The storeDetailsWithStoreName:... method you have defined is a good example of what Apple calls a factory method (assuming you aren't doing anything weird in its implementation). It's a quite common pattern; Foundation has all sorts of examples: arrayWithCapacity:, numberWithInt:, etc.
With ARC, the simplest examples of these factory methods are nearly identical to a corresponding alloc/init expression, since the developer no longer has to think about autoreleasing objects. But there are still plenty of uses for factory methods, e.g. special instantiation patterns such as singleton or flyweight, including a small amount of common conversion or formatting code for convenience, implementing class clusters, etc. And there's the simple convenience of not having an extra set of brackets and less indentation.
The instancetype keyword is a good choice. This allows you to send the same message to a subclass of Store, with the expectation that the method will instantiate an object of the subclass using the same init method, like this:
+ (instancetype)storeWithStoreName:(NSString *)storeName
telephoneNumber:(NSString *)
...
{
return [[self alloc] initWithStoreName:...];
}
In the code above, as it's a class method, the self in [self alloc] is the Class object (either Store or a subclass of Store) rather than a specific instance of Store. This is what allows creating an instance of the correct class at runtime, depending on whether you call [Store storeWithStoreName:...] or [MoreSpecificStoreSubType storeWithStoreName:...].
The alternative to a factory method, or compliment to it really, is to declare a custom init method in your Store class:
- (id)initWithStoreName:(NSString *)storeName
telephoneNumber:(NSString *)telephoneNumber ...
…and use that directly inside your loop, instead of a factory method. Again, with ARC, not much of a difference between the two unless there's extra work you want to do in the factory method. You can have multiple variants of the init method; the standard practice is for all of them to call the most detailed init method, which is called the designated initializer.
I would recommend taking the time to read the Apple documentation pages on standards for class design (I linked to some of these pages above). Since there are a lot of this is based more on convention rather than language design restrictions, it's important to know all about the patterns and best practices for good design and proper behavior of special methods.
Edit: This downvoting syndrom here sucks big time. I asked a question where I in my opinion showed that I did my homework, and asked for advice. The upvoted answers implied going with compile time warnings, whereas my own and probably most clean OOP way didn't receive any interest.
Brief overview in order to understand why I need this and what I try to do: I'm writing an ORM that implements the data mapper pattern. A mapper (i.e. for SQLite results) must create subclasses of an entity class - using the initializer of the base entity class. So there is the problem.
The mapper does not, and should not, know about specific classes. Mapping descriptions and specific mappers for different data sources are abstracted away from the entity class, and by design part of the entity description.
Entities are similar to NSManagedObject, though the ORM follows a different pattern. The description any entity is created with is similar to NSEntityDescription (but also following a different pattern and purpose).
So my goal is to create entities that I know are subclasses of ManagedEntity, using the init method of ManagedEntity.
So the init of my mapper looks like this:
- (id)initWithEntityClass:(Class)EntityClass entityDescriptor:(EntityDescription*)entityDescriptor
{
self = [super init];
if (self)
{
_EntityClass = EntityClass;
_entityDescription = entityDescription;
... (assert that class is of subclass of ManagedEntity)
}
And some time later in my mapper I then want to create the concrete entity:
-(void)createEntityWithSQLiteResultSet:(sqlite3_stmt*)resultSet
{
// Problem: How to init a class known to be a subclass of ManagedEntity?
ManagedEntity *newEntity = [[_EntityClass] alloc] initWithEntityDescription:_entityDescription];
}
So how do I create this child class of ManagedEntity, using the init of ManagedEntity?
Sure, I could use respondsToSelector() for initWithEntityDescription and invoke that. But something tells me there should be a more elegant way where the class kind is already known. Also, respondsToSelector and selector invocation will do a runtime check only. Even though the entity initializer should not change, it seems a bad choice to lose compile time checking if this method exists.
As part of your mapping, you must know what subclass you need. Then use
ManagedEntity *newEntity = [[NSClassFromString(className) alloc] initWithEntityDescription:_entityDescription];
EDIT:
I was building out this in a GitHub project as I promised and realized why it may not compile. You must have -initWithEntityDescription: declared in a known class that is accessible within the scope. In this case, it would mean that you must declare and implement ManagedEntity -initWithEntityDescription: and have have `#import "ManagedEntity.h" at the top of your file.
To reinforce Neal's correct answer that the OP claims cant work because he knows objC :)
#import <CoreData/CoreData.h>
#interface TestMapper : NSObject
- (NSManagedObject*)createClassForEntity:(NSEntityDescription*)entity context:(NSManagedObjectContext*)ctx;
#end
#import "TestMapper.h"
#implementation TestMapper
- (NSDictionary*)entityToClassMap {
return nil; //TODO ;)
}
- (NSManagedObject*)createClassForEntity:(NSEntityDescription*)entity context:(NSManagedObjectContext*)ctx {
NSString *className = self.entityToClassMap[entity.name];
assert(className);
return [[NSClassFromString(className) alloc] initWithEntity:entity insertIntoManagedObjectContext:ctx];
}
#end
alternative using the runtime you want
id cls = NSClassFromString(className);
id alloced_cls = objc_msgSend(cls, #selector(alloc));
id newEntity = objc_msgSend(alloced_cls, #selector(initWithEntity:insertIntoManagedObjectContext:), entity, ctx);
return newEntity;
What would be a nice pattern in Objective-C for class variables that can be "overridden" by subclasses?
Regular Class variables are usually simulated in Objective-C using a file-local static variables together with exposed accessors defined as Class methods.
However, this, as any Class variables, means the value is shared between the class and all its subclasses. Sometimes, it's interesting for the subclass to change the value for itself only. This is typically the case when Class variables are used for configuration.
Here is an example: in some iOS App, I have many objects of a given common abstract superclass (Annotation) that come in a number of concrete variations (subclasses). All annotations are represented graphically with a label, and the label color must reflect the specific kind (subclass) of its annotation. So all Foo annotations must have a green label, and all Bar annotations must have a blue label. Storing the label color in each instance would be wasteful (and in reality, perhaps impossible as I have many objects, and actual configuration data - common to each instance - is far larger than a single color).
At runtime, the user could decide that all Foo annotations now will have a red label. And so on.
Since in Objective-C, Classes are actual objects, this calls for storing the Foo label color in the Foo class object. But is that even possible? What would be a good pattern for this kind of things? Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.
Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.
Why do you think this would be ugly? It is a very simple approach since you can use [self className] as the key in the dictionary. It is also easy to make it persistent since you can simply store the dictionary in NSUserDefaults (as long as it contains only property-list objects). You could also have each class default to its superclass's values by calling the superclass method until you find a class with a value.
+ (id)classConfigurationForKey:(NSString *)key {
if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
Class c = [self class];
id value = nil;
while(value == nil) {
NSDictionary *classConfig = [_configurationDict objectForKey:[c className]];
if(classConfig) {
value = [classConfig objectForKey:key];
}
c = [c superclass];
}
return value;
}
+ (void)setClassConfiguration:(id)value forKey:(NSString *)key {
if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
NSMutableDictionary *classConfig = [_configurationDict objectForKey:[self className]];
if(classConfig == nil) {
classConfig = [NSMutableDictionary dictionary];
[_configurationDict setObject:classConfig forKey:[self className]];
}
[classConfig setObject:value forKey:key];
}
This implementation provides no checking to make sure you don't go over the top superclass, so you will need to ensure that there is a value for that class to avoid an infinite loop.
If you want to store objects which can't be stored in a property list, you can use a method to convert back and forth when you access the dictionary. Here is an example for accessing the labelColor property, which is a UIColor object.
+ (UIColor *)classLabelColor {
NSData *data = [self classConfigurationForKey:#"labelColor"];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+ (void)setClassLabelColor:(UIColor *)color {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
[self setClassConfiguration:data forKey:#"labelColor"];
}
my answer here may help:
What is the recommended method of styling an iOS app?
in that case, your annotation just holds a reference to a style (e.g. you need only one per style), and the size of a pointer for an entire style is not bad. either way, that post may give you some ideas.
Update
Jean-Denis Muys: That addresses the sample use case of my question, but not my question itself (a pattern to simulate class instance variables).
you're right, i didn't know how closely your example modeled your problem and i considered commenting on that.
for a more general and reusable solution, i'd probably just write a threadsafe global dictionary if your global data is nontrivial (as you mentioned in your OP). you could either populate it in +initialize or lazily by introducing a class method. then you could add a few categories to NSObject to access and mutate the static data -- do this for syntactical ease.
i suppose the good thing about that approach is that you can reuse it in any program (even though it may appear ugly or complex to write). if that's too much locking, then you may want to divide dictionaries by prefixes or create a simple thread safe dictionary which your class holds a reference to -- you can then synthesize an instance variable via the objc runtime to store it and declare an instance method to access it. the class method would still have to use the global data interface directly.
I am working on an object factory to keep track of a small collection of objects. The objects can be of different types, but they will all respond to createInstance and reset. The objects can not be derived from a common base class because some of them will have to derive from built-in cocoa classes like NSView and NSWindowController.
I would like to be able to create instances of any suitable object by simply passing the desired classname to my factory as follows:
myClass * variable = [factory makeObjectOfClass:myClass];
The makeObjectOfClass: method would look something like this:
- (id)makeObjectOfClass:(CLASSNAME)className
{
assert([className instancesRespondToSelector:#selector(reset)]);
id newInstance = [className createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
Is there a way to pass a class name to a method, as I have done with the (CLASSNAME)className argument to makeObjectOfClass: above?
For the sake of completeness, here is why I want to manage all of the objects. I want to be able to reset the complete set of objects in one shot, by calling [factory reset];.
- (void)reset
{
[managedObjects makeObjectsPerformSelector:#selector(reset)];
}
You can convert a string to a class using the function: NSClassFromString
Class classFromString = NSClassFromString(#"MyClass");
In your case though, you'd be better off using the Class objects directly.
MyClass * variable = [factory makeObjectOfClass:[MyClass class]];
- (id)makeObjectOfClass:(Class)aClass
{
assert([aClass instancesRespondToSelector:#selector(reset)]);
id newInstance = [aClass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
I have right a better tutorial on that , please checkout
https://appengineer.in/2014/03/13/send-class-name-as-a-argument-in-ios/
It's pretty easy to dynamically specify a class, in fact you can just reference it by it's name:
id string = [[NSClassFromString(#"NSString") alloc] initWithString:#"Hello!"];
NSLog( #"%#", string );
One other tip, I would avoid using the nomenclature 'managed object' since most other Cocoa programmers will read that as NSManagedObject, from Core Data. You may also find it easier to use a global NSNotification (that all your reset-able objects subscribe to) instead of managing a collection of different types of objects, but you're more informed to make that decision than I am.
The bit of the answer missing from the other answers is that you could define a #protocol containing your +createInstance and +reset methods.
It sounds like you want something like:
- (id)makeObjectOfClassNamed:(NSString *)className
{
Class klass = NSClassFromString(className);
assert([klass instancesRespondToSelector:#selector(reset)]);
id newInstance = [klass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
This would assume a class method named +createInstance. Or you could just use [[klass alloc] init].
To call it:
MyClass *variable = [factory makeObjectOfClassNamed:#"MyClass"];
Depending on what you're trying to do, it might be better to pass around class objects than strings, e.g.:
MyClass *variable = [factory makeObjectOfClass:[MyClass class]];