Is there any way to avoid this kind of code when overwriting the default setter for a retain property?
-(void)setMasterViewController:(UIViewController *)newMaster {
[newMaster retain];
[masterViewController release];
masterViewController = newMaster;
// do custom stuff on set
}
Is there any way to access the default setter, something like:
-(void)setMasterViewController:(UIViewController *)newMaster {
[defaultSetMasterViewController:newMaster];
// do custom stuff
}
This would keep the code DRYer. The way I'm doing it currently, the fact that it's a retain property is mentioned twice.
CoreData generates primitive setters, but in general there's no such affordance. You may be able to replace custom setters with key-value observing in some cases, but the solution to your specific question is probably "use ARC" if you can limit support to 10.6+/4.3+. It will handle the retain/release stuff on your behalf.
Not really, because the setter has to perform the actual setting. You could try doing this using key-value observing if you want to keep the original setter.
However... Yes, if you're using ARC! If you have a #property (strong), then when you simply say masterViewController = newMaster ARC will use objc_storeStrong, which:
Performs the complete sequence for assigning to a __strong object of non-block type. Equivalent to the following code:
id objc_storeStrong(id *object, id value) {
value = [value retain];
id oldValue = *object;
*object = value;
[oldValue release];
return value;
}
Related
I have an NSManagedObject subclass MyClass with a property myProp, which is defined #dynamic. There are various instances of reading myProp in my code, via [myClass myProp].
Now, I want to define a getter (that returns myProp after appending something to it) for myProp, without changing the various calls to [myClass myProp]. i.e. without creating a getter that is named something other than getMyProp.
My question is, if I create a getter getMyProp, which will override the getter created by NSManagedObject, how do I access the original value that is stored in the database?
To access the underlying values of a managed object you use the following two methods:
- (id)primitiveValueForKey:(NSString *)key
- (void)setPrimitiveValue:(id)value forKey:(NSString *)key
This is often used to convert NSNumber attributes into their 'real' type, for example a bool property:
- (BOOL)isShared
{
[self willAccessValueForKey:#"isShared"];
NSNumber *underlyingValue = [self primitiveValueForKey:#"isShared"];
[self didAccessValueForKey:#"isShared"];
return [underlyingValue boolValue];
}
The willAccessValueForKey: and didAccessValueForKey: are required by the underlying managed object class for handling faults and relationships etc.
And if you do end up writing a setter, you must also wrap the accessor in KVC methods:
- (void)setShared:(BOOL)isShared
{
NSNumber *newUnderlyingValue = [NSNumber numberWithBool:isShared];
[self willChangeValueForKey:#"isShared"];
[self setPrimitiveValue:newUnderlyingValue forKey:#"isShared"];
[self didChangeValueForKey:#"isShared"];
}
Having said this, I would personally not recommend you keep the same method name unless you have a good reason. For 'derived' values you generally want to create a brand new method with a different name. It doesn't take long to do a quick find/replace throughout your code.
EDIT: added willAccessValueForKey:/didAccessValueForKey: (thanks jrturton)
I can't figure out why my app is crashing after a few times I'm doing:
potionsT is nonatomic, retain, readonly.
-(void)First:(NSString*)Potions {
potionsT = [[NSString alloc] initWithString:Potions];
}
-(void)After:(NSString*)Potions {
[potionsT release];
potionsT = [[NSString alloc] initWithString:Potions];
You see, I'm first calling First and after that I'm calling a few times After: and wopes, it crashes. with ECX_BAD_ACCESS.. I'm pretty noob with all that memory manage thing, I know that... Thanks!
The point of retained property is it handles retain and release when you set it.
- (void)first:(NSString*)potions
{
self.potionsT = potions; // will automatically release old value and retain new
}
- (void)after:(NSString*)potions
{
self.potionsT = potions; // same as above
}
Also note how i renamed your Potions to potions, First: to first:, and After: to after:. Objective-C naming convention is to start variables and methods with lowercase letter, and class names with capital.
Did you #synthesize the property in the #implementation for the class? If so, then you should use:
self.potionsT = Potions;
if you use
potionsT = ...
then you are accessing the ivar, not the property. To access the property and let it do the memory management for you, you must precede it with an instance reference, and that can also be self.
If you use the property, you should not release potionsT manually, since the property already does that for you.
If you've set up potionsT as a property, you should access it that way:
-(void)first:(NSString*)potions {
self.potionsT = potions;
}
-(void)after:(NSString*)potions {
self.potionsT = potions;
}
In both cases, I changed your code to use the accessor for potionsT (that's what the self.potionsT means). I'm also not creating a new string, but just retaining the provided one. Since NSStrings are immutable, the result is the same. For best results, though, change the potionT property from retain to copy. That way, if a mutable string gets passed in, it'll be copied instead of retained (and immutable strings will still just be retained).
I was told by a fellow StackOverflow user that I should not use the getter method when releasing a property:
#property(nonatmic, retain) Type* variable;
#synthesize variable;
// wrong
[self.variable release];
// right
[variable release];
He did not explain in detail why. They appear the same to me. My iOS book said the getter on a property will look like this:
- (id)variable {
return variable;
}
So doesn't this mean [self variable], self.variable, and variable are all the same?
For a retained property with no custom accessor, you can release the object by:
self.variable = nil;
This has the effect of setting the ivar (which may not be called 'variable' if you have only declared properties) to nil and releasing the previous value.
As others have pointed out, either directly releasing the ivar (if available) or using the method above is OK - what you must not do is call release on the variable returned from a getter.
You can optionally write custom getter behavior, which may result in completely different behavior. So, you cannot always assume that [variable release] has the same results as [self.variable release].
As well, you can write custom properties without an exclusive ivar backing them... it can get messy fast if you start releasing objects from references returned by getters!
There may be additional reasons that I'm unaware of...
A typical getter will look more like this:
- (id)variable {
return [[variable retain] autorelease];
}
So if you use [self.variable release] you have an additional retain and autorelease that you don't really need when you just want to release the object and that cause the object to be released later than necessary (when the autorelease pool is drained).
Typically, you would either use self.variable = nil which has the benefit that it also sets the variable to nil (avoiding crashes due to dangling pointers), or [variable release] which is the fastest and may be more appropriate in a dealloc method if your setter has custom logic.
not all getters take this form:
- (id)variable { return variable; }
...that is merely the most primitive form. properties alone should suggest more combinations, which alter the implementation. the primitive accessor above does not account for idioms used in conjunction with memory management, atomicity, or copy semantics. the implementation is also fragile in subclass overrides.
some really brief examples follow; things obviously become more complex in real programs where implementations become considerably more complex.
1) the getter may not return the instance variable. one of several possibilities:
- (NSObject *)a { return [[a copy] autorelease]; }
2) the setter may not retain the instance variable. one of several possibilities:
- (void)setA:(NSObject *)arg
{
...
a = [arg copy];
...
}
3) you end up with memory management implementation throughout your program, which makes it difficult to maintain. the semantics of the class (and how it handles instance variables' ref counting) should be kept to the class, and follow conventions for expected results:
- (void)stuff:(NSString *)arg
{
const bool TheRightWay = false;
if (TheRightWay) {
NSMutableString * string = [arg mutableCopy];
[string appendString:#"2"];
self.a = string;
[string release];
// - or -
NSMutableString * string = [[arg mutableCopy] autorelase];
[string appendString:#"2"];
self.a = string;
}
else {
NSMutableString * string = [arg mutableCopy];
[string appendString:#"2"];
self.a = string;
[self.a release];
}
}
failing to follow these simple rules makes your code hard to maintain and debug and painful to extend.
so the short of it is that you want to make your program easy to maintain. calling release directly on a property requires you to know a lot of context of the inner workings of the class; that's obviously bad and misses strong ideals of good OOD.
it also expects the authors/subclassers/clients to know exactly how the class deviates from convention, which is silly and time consuming when issues arise and you have to relearn all the inner details when issues arise (they will at some point).
those are some trivial examples of how calling release on the result of a property introduces problems. many real world problems are much subtler and difficult to locate.
I have a property:
#property(readwrite, ?????) NSDate *selectedDate;
The accessors are like so:
NSCalendar _calendar;
NSDateComponents _selectedDateComponents;
#dynamic selectedDate;
- (void)setSelectedDate:(NSDate *)newDate
{
#synchronized(_selectedDateComponents)
{
if (!newDate) return;
[_selectedDateComponents release];
int requiredComponents = NSDayDateComponent | NSMonthDateComponent | NSYearDateComponents;
_selectedDateComponents = [[_calendar components: requiredComponents fromDate:newDate] retain];
}
}
- (NSDate *)selectedDate
{
#synchronized(_selectedDateComponents)
{
if (!_selectedDateComponents) return nil;
return [_calendar dateFromComponents:_selectedDateComponents];
}
}
The class doesn't keep a reference to the object that is sent to the setter. None of the retain, copy or assign seem appropriate. I like having this functionality encapsulated as a property but maybe a property is not appropriate.
What's your view?
If you are writing your own setters, it doesn't matter what you use. It only serves as a hint to how it works to yourself/other developers.
The property type only really affects the methods created by #synthesize. So if you provide your own methods, you dictate the retain strategy yourself, and the property strategy form the declaration is mostly ignored.
In this case I would use copy. Because, while you are not using a direct copy, you are storing value from that come from the passed in object and storing them in a non obtrusive way to that object. So you are copying the info out, just into a different format. But as far as the compiler cares, it doesn't really matter at all. It's purely for show when you write your own setter.
Suppose (for the sake of argument) that I have a view class which contains an NSDictionary. I want a whole bunch of properties, all of which access the members of that dictionary.
For example, I want #property NSString* title and #property NSString* author.
For each one of these properties, the implementation is the same: for the getter, call [dictionary objectForKey:propertyName];, and for the setter do the same with setObject:forKey:.
It would take loads of time and use loads of copy-and-paste code to write all those methods. Is there a way to generate them all automatically, like Core Data does with #dynamic properties for NSManagedObject subclasses? To be clear, I only want this means of access for properties I define in the header, not just any arbitrary key.
I've come across valueForUndefinedKey: as part of key value coding, which could handle the getters, but I'm not entirely sure whether this is the best way to go.
I need these to be explicit properties so I can bind to them in Interface Builder: I eventually plan to write an IB palette for this view.
(BTW, I know my example of using an NSDictionary to store these is a bit contrived. I'm actually writing a subclass of WebView and the properties will refer to the IDs of HTML elements, but that's not important for the logic of my question!)
I managed to solve this myself after pouring over the objective-c runtime documentation.
I implemented this class method:
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
NSString *method = NSStringFromSelector(aSEL);
if ([method hasPrefix:#"set"])
{
class_addMethod([self class], aSEL, (IMP) accessorSetter, "v#:#");
return YES;
}
else
{
class_addMethod([self class], aSEL, (IMP) accessorGetter, "##:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
Followed by a pair of C functions:
NSString* accessorGetter(id self, SEL _cmd)
{
NSString *method = NSStringFromSelector(_cmd);
// Return the value of whatever key based on the method name
}
void accessorSetter(id self, SEL _cmd, NSString* newValue)
{
NSString *method = NSStringFromSelector(_cmd);
// remove set
NSString *anID = [[[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:#""] lowercaseString] stringByReplacingOccurrencesOfString:#":" withString:#""];
// Set value of the key anID to newValue
}
Since this code tries to implement any method that is called on the class and not already implemented, it'll cause problems if someone tries calling something you're note expecting. I plan to add some sanity checking, to make sure the names match up with what I'm expecting.
You can use a mix of your suggested options:
use the #dynamic keyword
overwrite valueForKey: and setValue:forKey: to access the dictionary
use the objective-c reflection API's method class_getProperty and check it for nil. If it's not nil your class has such a property. It doesn't if it is.
then call the super method in the cases where no such property exists.
I hope this helps. Might seem a bit hacky (using reflection) but actually this is a very flexible and also absolutely "legal" solution to the problem...
PS: the coredata way is possible but would be total overkill in your case...
Befriend a Macro? This may not be 100% correct.
#define propertyForKey(key, type) \
- (void) set##key: (type) key; \
- (type) key;
#define synthesizeForKey(key, type) \
- (void) set##key: (type) key \
{ \
[dictionary setObject];// or whatever \
} \
- (type) key { return [dictionary objectForKey: key]; }
sounds like you should should be using a class instead of a dictionary. you're getting close to implementing by hand what the language is trying to give you.
There is a nice blog with example code with more robust checks on dynamic properties at https://tobias-kraentzer.de/2013/05/15/dynamic-properties-in-objective-c/ also a very nice SO answer at Objective-C dynamic properties at runtime?.
Couple of points on the answer. Probably want to declare an #property in the interface to allow typeahead also to declare the properties as dynamic in the implementation.