Could someone share some knowledge on whats best practice / code convention on using #property iVars in init methods or designated initializers?
please see my example:
#interface MyClass ()
#property(nonatomic,strong) nsstring *tempString;
#property(nonatomic,strong) NSMutableArray *arrItems;
#end
#implementation ViewController
- (id)init
{
if (self = [super init]) {
//Is this best practice / correct
_tempString = #"";
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
...
...
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
#end
Any advice on why one or the other should be used?
Thanks...
Apple's guidance on this topic is included in the aptly named section Don’t Use Accessor Methods in Initializer Methods and dealloc.
Read this thread: Why shouldn't I use Objective C 2.0 accessors in init/dealloc?
In other words if you are not goiung to use KVO you can use second approach:
//Or this
self.tempString = #"";
self.arrItems = [[NSMutableArray alloc] initWithCapacity:0];
But be care full with alloc-init, don't forget about autorelease.
It's typically better to use property notation when you define it, partly(mostly?) for the reason Jeremy mentioned.
Debugging a particular variable is a whole lot easier when you can set a breakpoint in method setter override and have it apply to ALL code paths that modify the variable.
Another reason is to keep a consistent memory management model, although it is less important since you are using ARC. If you weren't however, and strong was retain, then you would make sure that the object you are setting to the property is autoreleased everywhere you set the property, and not have to deal with releasing the current value if you are directly setting the variable.
Consistency is important for maintenance/readability and debugging, no matter what practices you use.
I prefer the lazy instantiation method for properties.
After you #synthesize you can override your getter to lazily instantiate your property
For Example:
-(NSString *)tempString {
if(!tempString) {
_tempString = #"";
}
return _tempString;
}
and
-(NSMutableArray *)arrItems {
if(!_arrItems) {
_arrItems = [[NSMutableArray alloc] initWithCapacity:0];
}
return _arrItems;
}
If you do want to set your property in the init method, use dot notation self.myProperty so that it uses the defined setter for the property and not the private class method directly.
According to Apple, you should not use accessors in init... or dealloc methods:
You should always access the instance variables directly from within
an initialization method because at the time a property is set, the
rest of the object may not yet be completely initialized. Even if you
don’t provide custom accessor methods or know of any side effects from
within your own class, a future subclass may very well override the
behavior.
Taken from this doc: Encapsulating Data.
Related
I have many "model" objects whose properties are defined as "readonly" and shared among various components.
In some cases I need to create local mutable copies of the objects (using them for local mutable state)
I rather not implement NSMutableCopy protocol as the object should be immutable after it is created. The modified object could be "passed" around after copy+mutate operations.
Is there a suggested mechanism , or should I just implement a constructor receiving the "changed" parameters?
For example an object which parses a JSON to native types :
#interface ImmutableObject : NSObject
// various "readonly" properties
...
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
#property (nonatomic, readonly) MyClass1 *prop1;
#property (nonatomic, readonly) MyClass2 *prop2;
...
#property (nonatomic, readonly) NSArray<MyClass100 *> *prop100;
#end
#implementation
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
self = [super init];
[self parseDictionaryToNative:jsonDictionary];
return self;
}
#end
Somewhere in code:
ImmutableObject *mutated = [immutableObject mutableCopy]; // best way to accomplish this?
// change some values...
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // save the new object
#spinalwrap is correct, but in this case there is no reason to create the extra copy before storing it. NSMutableArray is a subclass of NSArray, so can be used anywhere an NSArray can be used (and this is very common). Same for yours. In your particular case, you'd probably do it this way:
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = mutated; // Since `state` is an immutable type,
// attempts to mutate this later will be compiler errors
This is safe because you know that this block of code is the only block that has a reference to the mutable version of the object (because you created it here).
That said, once you've created a mutable subclass, you now need to consider the possibility that any ImmutableObject you are passed might actually be a MutableObject, and so make defensive copies (just as is done with NSArray, NSString, etc.) For example:
- (void)cacheObject:(ImmutableObject *)object {
// Need to copy here because object might really be a MutableObject
[self.cache addObject:[object copy]];
}
This is made fairly efficient by implementing copy on ImmutableObject and return self, and implementing copy on MutableObject as an actual copy, usually like this:
ImmutableObject.m
- (ImmutableObject *)copy {
return self;
}
MutableObject.m
// as in spinalwrap's example
- (MutableObject *)mutableCopy {
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
// No need to duplicate code here. Just declare it immutable;
// no one else has a pointer to it
- (ImmutableObject *)copy {
return (ImmutableObject *)[self mutableCopy];
}
So the copy is almost free if the object was immutable already. I say "fairly efficient" because it still causes some unnecessary copies of mutable objects that are never mutated. Swift's copy-on-write system for value types was specifically created to deal with this problem in ObjC. But the above is the common pattern in ObjC.
note that NSMutableArray, NSMutableData etc. are different classes than their immutable counterparts. So in this case, you maybe should define a MutableObject class with the same interface as the ImmutableObject class (but with mutable properties) and use that if you want to have a mutable object.
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // creates an ImmutableObject
the implementation of ImmutableObject's mutableCopy could be something like:
- (MutableObject *) mutableCopy
{
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
and MutableObject's copy method could look like this:
- (ImmutableObject *) copy
{
ImmutableObject *instance = [ImmutableObject new];
instance.prop1 = [self.prop1 copy];
// etc...
return instance;
}
You're not forced to use the NSMutableCopy protocol formally, but you can.
i would like to add extra data to EKEvent, i tried NSDictionary (there is a lot of data to add) but it doesn't work..
sample code:
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[eventStore setValue:dictionary forKey:MAIN_DICTIONARY];
any ideas?
You're using setValue:forKey: in a wrong way. That a look here. There are different options to achieve what you want: category, subclassing or create a class that contains the EKEvent and the NSMutableDictionary. It depends on how you need to use the EKEvent.
You cannot do it this way, because even with key-value coding you can only set (declared or non declared) properties known by the instance. Basically the accessors (setter, getter) are executed. But there is no property MAIN_THREAD,no setter setMAIN_THREAD: in EKEvent.
If you want to extend instances of a foreign class that are created by the system (the instances, not the class), there are to ways to add data:
You create an own class, let's say MyEvent and give them a reference to the system instance (EKEvent) as a property plus the properties you need. When you get an instance of EKEvent you look-up your list of MyEventss using the identifier. With that you have the full access to your data.
You use associated objects. But you have to take care that they are not handled by the instance, i. e. while copying.
The first solution is better by far. Simple sample code:
#interface MyEvent : NSObject
#property (readonly) EKEvent* systemEvent;
#property id customProperty;
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent;
#end
#implemenation MyEvent
// Look-Up
NSMutableDictionary *eventLookUp;
+ (void)initialize
{
if( self == [MyEvent class])
{
eventLookUp = [NSMutableDictionary new];
}
}
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent
{
return eventLookUp[systemEvent.calendarItemIdentifier];
}
// Instance creation
- (instancetype)initWithSystemEvent:(EKEvent*)systemEvent
{
// Usual initializer
…
eventLookUp[systemEvent.calendarItemIdentifier] = systemEvent;
return self;
}
+ (instancetype)newEventWithSystemEvent:(EKEvent*)systemEvent
{
return [[self alloc] initWithSystemEvent:systemEvent];
}
#end
Typped in Safari
I'm still fairly new to Objective C so please bear with me:
My app has a Delegate, a Navigation Controller, and a View.
I also use a Singleton for "global" variables.
I know I can do this but it seems ungainly:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
-(void)myMethod{
GlobalData *globDat=[GlobalData getSingleton];
junk=globDat.someValue;
}
-(void)myOtherMethod{
GlobalData *globDat=[GlobalData getSingleton];
moreJunk=globDat.someOtherValue;
}
I'd like to this but it can't:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
GlobalData *globDat=[GlobalData getSingleton]; //Compiler won't allow this line
-(void)myMethod{
junk=globDat.someValue;
}
-(void)myOtherMethod{
moreJunk=globDat.someOtherValue;
}
However I can do this:
#import "GlobalData.h"
#synthesize ...
NSInteger junk;
NSInteger moreJunk;
GlobalData *globDat;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
globDat=[GlobalData getSingleton];
}
return self;
}
-(void)myMethod{
junk=globDat.someValue;
}
-(void)myOtherMethod{
moreJunk=globDat.someOtherValue;
}
So, is there a standard/common/proper "init" method to use for all Classes?
Should I just do this in every Class?
-(id)init{
if(self=[super init]){
globDat=[GlobalData getSingleton];
}
return self;
}
I know I can do this but it seems ungainly...
You seem to be asking how to do away with the part where you retrieve the value you want from your singleton. The best way to do that is to eliminate the singleton in the first place.
You say that you have an app delegate, a navigation controller, and a view. You probably also have a view controller. If those are the main objects in your application, you might consider storing your data in your view controller. Or, convert your singleton into a legitimate data model, and let your view controller keep a reference to it in a property. Then you can say something like:
-(void)myMethod{
junk = self.model.someValue;
}
which is pretty close to what you seem to be asking for.
It depends on how much you will use GlobalData.
extensive use in many methods
If you plan to make an extensive use of GlobalData in your class (in many methods), then, you should better add
#propertu (nonatomic, readwrite, weak)GlobalData *globalData in your .h
(or, better, in the class extension). In your main init... method, you set the value self.globalData = [GlobalData getSingleton];. You can also do as you did, with an instance variable, GlobalData* myGlobalData in your .h or in the class extension.
Make sure your init... method starts with init and make sure there is a "main" init... method (we call it the "designated initializer"). You can simply override the init method of NSObject. But, if you need, you can define
- (id)initWithString:(NSString*)str
{
self = [super init] ; // or another one ; here you should use the designated init. of the parent class
if (self){
self.string = str ;
self.globalData = [GlobalData getSingleton] ;
}
return self;
}
extensive use in few methods
If you intensively use GlobalData in one or two methods, you don't need to do so. You will just define a local variable GlobalData * globalData = [GlobalData getSingleton]; and use it.
The third code you propose is not objected-oriented.
I'm not 100% sure what you're question is really about (singletons as a common pattern vs common -init method?), but it is common to create what is called a designated initializer for your class. If you're not using a singleton pattern an init method signature that takes a value might look like this:
-(id)initWithValue:(NSString*)newValue;
As for using a singleton pattern to provide access to your variables I would recommend a more loosely coupled approach, especially if the number of values your singleton class manages begins to grow. I would put those variables into their own classes (separated logically) and use them as needed, by passing them to constructors, or instantiating them. That's an opinion/philosophy issue, though.
I ended up using this to resolve the question.
(Again I was primarily trying to clean out the redundancy of code)
In my Class modules I did the following:
#import "GlobalData.h"
GloablData *globDat;
-(id)init{
if(self=[super init]){
globDat=[GlobalData getSingleton];
}
return self;
}
...
-(void)someMethod{
int junk=globDat.junkvalue;
}
Well I'm just confused when the lazy instantiation should be used.
I understand the basic concept of lazy instantiation though.
" I understand that all properties start out as nil in Objective-C and that sending a message to nil does nothing, therefore you must initialize using [[Class alloc] init]; before sending a message to a newly created property. "(Lazy instantiation in Objective-C/ iPhone development)
m.file:
#property (strong, nonatomic) NSMutableArray *cards;
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
} }
Well, what I really don't get is when I'm supposed to use this type of instantiation?
Mostly I see the code like this:
h.file:
#interface Card : NSObject
#property (strong, nonatomic) NSString *contents;
m.file:
if([card.contents isEqualToString:self.contents]){
score = 1;
}
*This might be a stupid question but I'm really confused. I'm new here, Thanks.
There is no reason to use Lazy Instantiation/Lazy Initialization if you find it confusing; simply initialize your instance variables/properties in the class init methods and don't worry about it.
As the object is created as a side-effect of calling the getter method, it's not immediately obvious that it is being created at all, so one alternative, which would also mean you can use the default compiler-generate getter method, is to explicitly check for it in addCard:
- (void)addCard:(Card *)card
atTop:(BOOL)atTop
{
if (!self.cards)
self.cards = [NSMutableArray new];
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
(and removing the user-supplied getter method)
However the net-effect is the same as the code you posted, with the exception that self.cards will return nil until addCard is called, however I doubt this will cause a problem.
When using dot notation to access your instance variables, you are calling your getter method for that given property. Therefore, by using dot notation and lazy instantiation, your getter will always assert that a property is not nil before you send it a message. Therefore, code such as
[self.cards insertObject:card atIndex:0];
will actually call the getter at self.cards; if you use dot notation on your objects and program the getters accordingly, you will always ensure that your instance variables are allocated and initialized, while simultaneously cleaning up your init method for code that is much more important.
Lazy instantiation is a common practice among Objective-C programmers; I suggest getting into the flow of the convention.
EDIT: thanks for Raphael mentioning this in a comment previously.
Lazy instantiation is a performance enhancement in certain types of scenarios. One example would be a class that has a very expensive user facing UI string.
If you create many of instances of that class but only a very small subset of those instances will be shown in your UI, you waste a lot of CPU resources creating a very expensive UI string that rarely will be used.
I have a MKPolyline subblass which I want to implement NSCoding, i.e.
#interface RSRoutePolyline : MKPolyline <NSCoding>
I asked a question on the best way to encode the c-array and got an excellent answer. However, there is no init method defined on MKPolyline, i.e. there is no other way to give it data other than its class method polylineWithPoints:points.
Is this code where my comment is ok?
- (void)encodeWithCoder:(NSCoder *)aCoder
{
MKMapPoint *points = self.points;
NSUInteger pointCount = self.pointCount;
NSData *pointData = [NSData dataWithBytes:points length:pointCount * sizeof(MKMapPoint)];
[aCoder encodeObject:pointData forKey:#"points"];
[aCoder encodeInteger:pointCount forKey:#"pointCount"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSData* pointData = [aDecoder decodeObjectForKey:#"points"];
NSUInteger pointCount = [aDecoder decodeIntegerForKey:#"pointCount"];
// Edit here from #ughoavgfhw's comment
MKMapPoint* points = (MKMapPoint*)[pointData bytes];
// Is this line ok?
self = (RSRoutePolyline*)[MKPolyline polylineWithPoints:points count:pointCount];
return self;
}
You should call an init method on any subclass of NSObject. Since MKPolyline is an NSObject, you should init it.
But MKPolyline has no methods and no init. This is Objective C's was of telling you that you can't subclass it.
Instead, as WDUK suggested, define your own class. It keeps track of your list point points, and manages NSCoding to save and restore them as needed.
#interface RSPolyline: NSObject<NSCoding>
- (id) initWithPoints: (NSArray*) points;
- (id) initWithCoder:(NSCoder *)aDecoder;
- (void) encodeWithCoder:(NSCoder *)aCoder;
- (MKPolyline*) polyLine;
#end
Your class can generate a polyline on request, perhaps caching the result if performance is an issue.
As a rule, don't reach for inheritance first. When you want to extend and improve a class, think first of composition.
It's dirty not to call [super init], and it doesn't bode well with my idea of good programming. Without calling super yourself, it isn't a true subclass; just a bastardization of composition that relies on a side effect of calling a convenience constructor. Saying this, I believe your method described will work OK, but it goes against the grain of good Objective-C programming and its conventions.
What I would suggest is to use MKPolyLine as an MKPolyLine instance, and use a category to add the extra bells and whistles you need. As for adding extra instance variables and such, you can use associated objects. An introduction to this concept can be found here, and this SO question addresses the use of them with categories: How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?
While it is generally allowed to create and return a different object in an init method, there are three problems with that line (explained below). Instead of this, I would suggest overriding the points and pointCount properties so that you can return values stored in an instance variable, and call the super implementation there if the instance variable is empty. Then, your initializer just sets these instance variables so that they will be used.
- (MKMapPoint *)points {
if(myPointsIvar == NULL) return [super points];
else return myPointsIvar;
}
// similarly for pointCount
The first problem is that you are creating a new object, but not releasing the old one, which means you are leaking it. You should store the result in a different variable, then release self, then return the result (you don't need to store it in self).
Second, polylineWithPoints:count: returns an autoreleased object, but initWithCoder: should return a retained one. Unless there is another retain on it, it could be deallocated while you are still using it.
If these were the only problems, you could solve both like this:
MKPolyline *result = [MKPolyline polylineWithPoints:points count:pointCount];
[self release];
return [result retain];
However, there is a third problem which cannot be solved so easily. polylineWithPoints:count: does not return a RSRoutePolyline object, and the object it returns may not be compatible with your subclass's methods (e.g. it probably won't support NSCoding). There really isn't a way to fix this, so you can't use polylineWithPoints:count:.