Sorry if this should be obvious but I'm not finding the answer.
This is part of some code I am using and I save the reference to tempData and the length. I need to know how to use that reference and the length to later retrieve the CGPoints. Is this possible? Thanks for any help!
- (void)setData:(float *)theData length:(int)length {
CGPoint *tempData = (CGPoint *)calloc(sizeof(CGPoint), length);
for(int i = 1; i < length - 1; i++) {
tempData[i] = CGPointMake(i, theSampleData[i]);
}
}
Ok, so you're dynamically allocating memory and using it to store values. Sure, you can do that.
Instead of making your setData method create a local variable tempData, make tempData an instance variable of your class. Set it to nil in your init method. Also make your length variable an instance variable and initialize it to zero.
In your setData method, allocate the pointer and save your data to it. Also save the length.
Then, anywhere else in your class, you can simply index into your tempData variable, just like you are doing to set the values.
Write a dealloc method for the class that checks to see if tempData is nil. If it's not, free it.
This is basic C memory management using malloc'ed memory (or calloc'ed memory - same thing) and pointers.
You allocate the space, but the pointer to the new storage is local in scope and will be lost.
You want to use a property (or instance variable) to store the value.
the preferred method is to use a class extension inside your .m file to keep that information private:
#interface MyClass ()
#property (nonatomic, assign) CGPoint *points;
#property (nonatomic, assign) NSUInteger pointCount;
#end
You should also implement
- (void)dealloc {
[super dealloc];
free(points);
points = NULL;
}
Related
In my child view controller, I have a property defined as:
#property (nonatomic, copy) NSString *name;
In view controller A, the Parent, I have the following:
NSString *temp = currency.name; //This is because currency is a Core Data Managed Object.
//I wanted to make sure it wasn't a confounding factor.
childViewController.name = temp;
if(childViewController.name == temp)
NSLog(#"I am surprised");
The problem is that if statement finds equivalency and the "I am surprised" is printed. I thought that == should be checking if they're the same object, and that the use of copy in the property declaration should ensure the setter is making a copy. I checked in the debugger and they are both pointing to the same string. (Which I believe is immutable, which may be why this is happening?)
The same thing happens even if I write childViewController.name = [temp copy];, which I find shocking!
Can anyone explain what is going on here?
Edit: I removed a bit here on worrying about a circular reference which I realized wasn't a concern.
This is an optimization.
For immutable objects, it's superfluous to create an actual copy, so - copy is often implemented as a simple retain, i. e.
- (id)copy
{
[self retain];
return self;
}
Try assigning a mutable object (e. g. NSMutableString) to the property, and you will get the "expected" behavior.
I used to do this till once I found the retain count of one of my retained propery is zero before dealloc function. (This situation is normal or abnormal?)
NOTE: It's a RC condition, not ARC.
For example, I got 4 retained properties below, should they always be released in dealloc function?
If not, how could I know when to release, and when not to release? Manually judge the retainCount?
#property (nonatomic, retain) NSString *fileName;
#property (nonatomic, retain) UIImage *fullSizeImage;
#property (nonatomic, retain) UIImage *thumbnailImage;
#property (nonatomic, retain) UIImageView *checkedImageView;
- (void)dealloc {
[checkedImageView release];
checkedImageView = nil;
[fileName release];
fileName = nil;
[fullSizeImage release];
fullSizeImage = nil;
[thumbnailImage release];
thumbnailImage = nil;
[super dealloc];
}
Well, if the question is "always?", then Wain is almost right...
a SHORT answer is YES...
because in general, when someone set-up a property, it means he's going to use it as a property, that is he uses its setter method to initialize it.
BUT (LONG answer): NO, NOT ALWAYS:
what if you, somewhere in your code, initialize the private var associated to the property without it's setter method? Keep in mind that a property is not a var, but just a useful way to get methods from Xcode to get and set a var associated to it.
in other words, when you write in .h:
#property (nonatomic, retain) NSString *fileName;
and in .m:
#synthesize fileName;
you are declaring a var called fileName and are asking xcode to create 2 (invisible) methods for you:
a setter, used to set a new retained value in fileName:
-(void)setFileName:(NSString *)newString{
if (fileName == newString) {
return;
}
NSString *oldString = fileName;
fileName = [newString retain];
[oldString release];
}
and a getter, used to get the value of fileName:
-(NSString)fileName{
return fileName
}
so, when you somewhere in your code use:
self.fileName = #"ciao";
you are using the property setter method, exactly as if you'd call it directly (and you can do it, the invisible method setFileName: really exist):
[self setFileName:#"ciao"];
doing so, as you can see in the setter method, from now on fileName is retained, and so you should release it in dealloc.
BUT, to answer your question:
if you use the dot rule to set a new string in your var, ok, everything is fine,
but you may decide to set it in the standard way, somewhere, maybe just for mistake:
fileName = #"ciao";
// code
fileName = #"Hallo";
// code
fileName = #"Bye";
this way you are not using the property setter method, but you are using the var directly, and so fileName is not retained, and if you try to release it, well you may get a crash...
PS:
Manually judge the retainCount?
no, never do that
Yes, they should always be released in dealloc. If you get to dealloc and something is already released and not set to nil then you did something wrong with your memory management elsewhere in the app.
Technically in dealloc you don't need to set to nil after releasing but setting to nil after releasing is a generally good idea.
Your dealloc is unnecessarily calling the getter for each property and then immediately releasing it. Just assign nil to release the properties:
- (void)dealloc {
self.checkedImageView = nil;
self.fileName = nil;
self.fullSizeImage = nil;
self.thumbnailImage = nil;
[super dealloc];
}
Although if you are following the current trend of letting clang auto-generate your backing instance variables, then this is better, as it won't cause KVO side-effects:
- (void)dealloc {
[_checkedImageView release];
[_fileName release];
[_fullSizeImage release];
[_thumbnailImage release];
[super dealloc];
}
Yes, they should normally all be released. If you have a retain count of zero, that usually means you've made a mistake somewhere in your memory management code.
You ask: If not, how could I know when to release, and when not to release? Manually judge the retainCount?
Possibly, but you could also let Xcode help you, using static analysis. Go to Product -> Analyze. It will quite often help you find erroneous releases, etc.
When to release? Quite obviously, if your object was holding a reference to another object, and your object goes away, then it should stop holding a reference to the other object. Why would you even look at the retain count? Retain count is about other people holding on the same object, but they are none of your business. They should know what they are doing. So you release the object. You do your job; everyone else has to do theirs. The easiest way, as others said, is to assign
self.someproperty = nil;
If your object was the only one holding a reference, that other object will go away. If others held a reference, it won't go away. Just as everyone would expect. The "release" method should be the only one ever caring about what the retain count of an object is.
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:.
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.
Please kindly point out what is wrong in my code. I define a variable idleTimer of my custom type
#property (nonatomic, retain) IdleTimer *idleTimer;
Then when I run the following codes, it crashes.
IdleTimer *idleTimerTemp = [[IdleTimer alloc] initTimer:PERIOD_COUPON_POPUP];
idleTimer = idleTimerTemp;
NSLog(#"Pt. 1 %d %d", [idleTimerTemp retainCount], [idleTimer retainCount]);
[idleTimer setDelegate:self];
[idleTimerTemp release];
NSLog(#"Pt. 2 %d %d", [idleTimerTemp retainCount], [idleTimer retainCount]);
If the idleTimer is used again, it crashes.
But it I retain the idleTimerTemp on "idleTimer = idleTimerTemp". No crash at all.
But my variable is defined as retain. what is wrong ?
Your property is defined as retain but that doesn't do anything with respect to the instance variable which is the backing storage for the property. Presumably somewhere in your code, you've done an #synthesize idleTimer;. That creates an accessor pair which implements the memory management you requested.
The only way to get that behavior is to call the accessor itself. So if you assign directly to idleTimer, that does nothing, but if you use self.idleTimer = idleTimerTemp, then the -setIdleTimer: accessor gets called, which will retain the parameter.
You're assigning to the idleTimer ivar directly instead of using the accessors. Do this instead:
self.idleTimer = idleTimerTemp;
The property accessors can't implement the semantics you specify if you don't use them.