Do I need to retain objects I receive in arguments? - cocoa

If I have a method with input that is used do I have to retain?
- (void) exampleMethod: (NSString *)input {
self.hey = [input retain];
}
What if I use input more than once?

Read the Memory Management Rules. If hey is a property with the retain or copy attributes set, then you do not need to invoke -retain on it (you can just do self.hey = input).

You don't need to retain a parameter that you only intend to use during the method. If you are going to keep a reference to it longer (as you seem to be in your example), then in most cases you should.
However, if you are using a property (which again you seem to be here), you should be managing the memory within the property setter itself, not calling retain explicitly when calling the setter.

In this case, the assignment to the .hey property a retain is implict in the accessor method.
Accessor Methods
If you want to continue using the string without using an accessor method, you may need to retain the string and the scope with which you need to have it available.

Related

How do automatically generated instance variables work in NSManagedObjects?

Xcode 4.5 and later auto-synthesizes properties, making an instance variable with the underscore prepended on the property name. But how does this work in an NSManagedObject? They want you to use KVC primitive methods in your custom setters. So what happens if you set an instance variable via the underscore ivar inside the NSManagedObject? Won't that screw things up since it would bypass the KVC methods? Or is it safely doing this behind the scenes?
If you access the underscore instance variable directly, you are bypassing the work that NSManagedObject does for you. You should use the get and set accessor methods that NSManagedObject auto-generates for your attributes.
Apple's documentation states
When you access or modify properties of a managed object, you should
use these [accessor] methods directly.
You can implement your own accessor methods if required, but in that case, you have to do additional work beyond changing the value of the instance variable:
You must ensure that you invoke the relevant access and change
notification methods (willAccessValueForKey:, didAccessValueForKey:,
willChangeValueForKey:, didChangeValueForKey:,
willChangeValueForKey:withSetMutation:usingObjects:, and
didChangeValueForKey:withSetMutation:usingObjects:).
This should illustrate that you can't get the correct behavior simply by modifying the instance variable directly.
Note that unlike ordinary properties, NSManagedObject properties are not synthesized at compile time (hence the use of #dynamic for the implementation). Since compile-time synthesis isn't used, there are no synthesized instance variables available for you to set.
Instead, instances of NSManagedObject have a private internal instance of something similar to an NSMutableDictionary to store their state. The dynamically generated property accessors are wrappers for calls to KVC-like methods that access the private storage.

implementing custom accessor methods

I am reading "Core Data Programming Guide". It contains this text:
You must, however, change attribute values in a KVC-compliant fashion.
For example, the following typically represents a programming error:
NSMutableString *mutableString = [NSMutableString stringWithString:#"Stig"];
[newEmployee setFirstName:mutableString];
[mutableString setString:#"Laura"];
For mutable values, you should either transfer ownership of the value
to Core Data, or implement custom accessor methods to always perform a
copy. The previous example may not represent an error if the class
representing the Employee entity declared the firstName property
(copy) (or implemented a custom setFirstName: method that copied the
new value). In this case, after the invocation of setString: (in the
third code line) the value of firstName would then still be “Stig” and
not “Laura”.
Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?
Question regarding copy and programming practice:
From what I have read here:
NSString property: copy or retain?
I understand
that using copy will ensure that firstName is "Stig", not Laura
it is wise to do so because "in almost all cases you want to prevent mutating an object's attributes behind its back"
I would really like to know what is the above quoted text trying to tell us in the context of Core Data. We have to use "copy" anyway whether using Core Data or not. Also, I would be glad if someone could throw more light on point "2" (it is wise to...) above as in what will be the consequences of mutating an object's attributes behind its back?
your "Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?"
mis-matched the point that Apple document wants to explain, I believe.
As Apple document points out, if custom-accessor-method is implemented normally, the default implementation does NOT copy attribute values. If the attribute value may be mutable and implements the NSCopying protocol (as is the case with NSString, for example), you can copy the value in a custom accessor to help preserve encapsulation (for example, in the case where an instance of NSMutableString is passed as a value).
Here is a copying setter snippet
#interface Department : NSManagedObject
{
}
#property(nonatomic, copy) NSString *name;
#end
#implementation Department
#dynamic name;
- (void)setName:(NSString *)newName
{
[self willChangeValueForKey:#"name"];
// NSString implements NSCopying, so copy the attribute value
NSString *newNameCopy = [newName copy];
[self setPrimitiveName:newNameCopy];
[self didChangeValueForKey:#"name"];
} #end
The issue is when to use (and how) immutable values.
Since core data use KVO heavily when detecting changes done to objects, if you use a mutable property that is changed directly through it object and not through the property, CoreData will not detect the change to the object and your changes might not persist to the store.
If you use mutable NSManagedObject attributes, override the setter/getter method and use only them to mutate the underlying object (this mean that you are responsible to let CoreData know that a change did happen to the object, and it must be persisted to the store.
Also, if you use transformable properties for complex objects, you must trigger the change notifications yourself in order for CoreData to realise that a change has occurred, and the object should be re-transformed and saved when the context saves.
I would highly recommend that when it comes to simple objects like strings, you use immutable property values which will force you to go through the object properties and trigger the default KVO notification (copy attributes will also force the KVO notifications).

Key-Value Coding and methods calling

It's a question about good programming techniques with Cocoa.
When you want to call a method on one property of your class, should you use KVC to get the receiver or just put the name of your property?
Example, KVC:
[[self property] myMethod];
Example, simple:
[property myMethod];
Thanks!
Example, KVC:
[[self property] myMethod];
That isn't KVC. The KVC way is:
[[self valueForKey:#"myProperty"] myMethod]
There is no reason to do this when you know the property at compile time; you can just ask for the property value or the ivar value directly. With KVO and (on the Mac) Bindings already implemented, there's not much reason to use KVC directly, as KVO and Bindings use it for you.
Example, simple:
[property myMethod];
That doesn't access the property; it accesses the ivar.
You're only accessing the property when you send an accessor message to the property's holder (self in your examples). It doesn't matter whether you use [self property] or self.property, as they're equivalent; either one is a property message to self, with whatever side effects that implies.
That's the key difference: Hitting the accessor may cause side effects, whereas accessing the ivar directly never will.
Hence, the best practice: Use the property in all your instance methods (as you probably want the accessors' side effects), except in init methods and dealloc, where side effects would be a bad thing. (As a general rule, you should not send messages to a half-initialized or half-deallocked object. The exception is when you explicitly commented the method as being part of your init/dealloc process and therefore wrote it to be safe to use in such circumstances.)
I believe the formal version is technically correct as that will guarantee any side-effects from a funky getter. (To make sure, make a custom getter that includes NSLog("in getter!") and let us know if it works.)
For setting you have to use the [self setProperty:foo]; as property = foo bypasses the setter and can lead to memory leaks.
If it feels more natural to you, the dot notation (e.g., self.property and self.property = foo) is identical to [self property] and [self setProperty:foo].

Best practice for copying private instance vars with NSCopying

I might be missing something obvious here, but I'm implementing NSCopying on one of my objects. That object has private instance variables that are not exposed via getters, as they shouldn't be used outside the object.
In my implementation of copyWithZone:, I need alloc/init the new instance, but also set up its state to match the current instance. I can obviously access current private state from inside copyWithZone:, but I can't set it into the new object, because there are no accessors for that state.
Is there a standard way around this while still keeping data privacy intact?
Thanks.
First, you should always have getters, even if they're private. Your object should only access even its own ivars using accessors (except in a very small number of cases). This will save you a great deal of suffering over memory management.
Second, Alex's suggestion of using -> is a standard approach, even though this violates the getters rule above. There are a small number of exceptions to that rule, and copy is one of. Using private setters here is still reasonable (and I used to do it that way exclusively), but I've found for various reasons that using -> often works out cleaner.
Be very careful to get your memory management correct. If you need to call [super copyWithZone:], then you should also read up on the complexities of NSCopyObject() and how it impacts you even if you don't use it yourself. I've discussed this at length in "NSCopyObject() considered harmful."
You can access the instance variables of the copy directly. You use the same pointer dereferencing syntax you would use with a struct. So, for example, if your class is this:
#interface MyCopyableClass : NSObject {
int anInstanceVariable;
}
#end
You can do this:
- (id)copyWithZone:(NSZone *)zone {
MyCopyableClass *theCopy = [[[self class] allocWithZone:zone] init];
theCopy->anInstanceVariable = anInstanceVariable;
return theCopy;
}
One option is to create a custom initializer that accepts the private iVar values. So you create it like:
-(id) initWithPropertyOne:(SomeClass *) anObject andPropertyTwo:(SomeClass *) anotherObject;
When you instantiate the copy, just use the custom initializer.

Passing NSMutableArray to other classes

I have created an NSMutableArray in the implementation of my class loginController. The mutable array contains a set of strings. I want to pass the mutable array with its objects to other classes within my cocoa-project. What is the best way to pass the array?
The most basic case is your login controller simply handing a snapshot of the array to the other controller. In this case, your login controller will need to have references to instances of the other classes, and it will set some property of those instances to the array. Remember to declare the properties with the copy attribute, so that the receivers don't hold on to your private mutable array.
If you want the other controllers to be able to modify the array, don't let them have your mutable array—that's an invitation to hard-to-find bugs.
Instead, you'll need to implement one property on the login controller, instead of one property on each of the other controllers. The login controller's property should have at least a getter and setter (which you can #synthesize), but you can implement more specific accessor methods for efficiency.
Once you have this property, the other controllers should access the property in a KVO-compliant way. If you implement the specific accessors, they can just use those. Otherwise, they'll need to send mutableArrayValueForKey: to the login controller. When they access the contents of that proxy array, they really access the login controller's array; when they mutate the proxy array, they mutate the login controller's array in turn.
Next comes the actual KVO part. You'll want the other controllers to know when one of them (or the login controller) changes the property. Have each controller (except the login controller) add itself as an observer of the property of the login controller. Remember to have them remove themselves in their -dealloc (or -finalize) methods.
In order for the right notifications to get posted, everything needs to use either accessors or mutableArrayValueForKey:. That goes for the login controller itself, too—it should use its own accessors when mutating the array, instead of messaging the array directly. The only exceptions are in init and dealloc (because the accessor messages would be messages to a half-inited/deallocked object, which will be a problem if you ever make the accessors fancy*).
BTW, it sounds like you may have way too many controllers. See if you can't move some of your logic into model objects instead. That drastically simplifies your code, as Cocoa is designed to work with a model layer. Being controller-heavy is fighting the framework, which makes more work for you.
*By “fancy”, I mean doing things other than or in addition to the normal behavior of a given accessor method. For example, insertObject:in<Foo>AtIndex: normally just tail-calls [<foo> insertObject:atIndex:]; if you insert or store the object somewhere other than in an array in an instance variable, or if you do something else in the same method (such as tell a view that it needs to display), then your accessor method is fancy.
short answer that may not be the best practice:
[otherObject giveArray:[NSArray arrayWithArray:theMutableArray]];
the question is a good one, but not complete... do you just need to pass an array of strings or does the class you are passing to need to modify the array?
In general, it's not a problem to simply pass around an NSMutableArray*, however you need to be careful, because you are just passing a pointer ( so if you retain it somewhere, you need to be aware that the owner or some other class may modify the array ).
generally spoken you would want to use NSMutableArray to dynamically build up an array of objects and when you need to share them, then make a non-mutable copy and pass that along.
NSMutableArray* myArr = [NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"four",nil];
// maybe modify the array here...
NSArray* nonMut = [[myArr copy] autorelease];
[someObject doWork:nonMut];
|K<
I think the pattern that's best for your situation is delegation. Your LoginController shouldn't have to know what class it's sending this data to. Instead, you would implement a LoginControllerDelegate protocol
#protocol LoginControllerDelegate <NSObject>
#optional
- (void)loginController:(LoginController *)loginController didReceiveLoginIDs:(NSArray *)ids;
#end
Then, in your LoginController class, you would implement a delegate property like this:
#property (nonatomic, assign) id <LoginControllerDelegate> delegate;
Then, when you've actually got something to communicate to the delegate, you would write this:
if ([self.delegate respondsToSelector:#selector(loginController:didReceiveLoginIDs:])
[self.delegate loginController:self didReceiveLoginIDs:[NSArray arrayWithArray:loginIDs]];
The object that should receive the login IDs would incorporate the LoginControllerDelegate protocol like this:
#interface SomeOtherClass : NSObject <LoginControllerDelegate>
And you would implement the loginController:didReceiveIDs: method in SomeOtherClass.
This way, instead of your LoginController needing to have intimate knowledge of the other classes in your project, you simply establish a mechanism for sending that data to whatever object is interested in it when it becomes available. If you later change which object should receive the login IDs, you only need to choose a different delegate.

Resources