Weird Memory Management Occurrence iOS - objective-c

For some reason in my below code, the replies array is NSLogging the correct description, but the comment.replies array is NSLogging null.
I immediately presumed that this was due to a memory management issue within my code, but I don't believe that that is true.
Please can you tell me why this is occurring?
- (TBComment *) dictionaryToComment:(NSDictionary *)dict {
TBComment *comment = [[TBComment alloc] init];
[comment setBody:[dict objectForKey:#"body"]];
[comment setCommentID:[dict objectForKey:#"id"]];
[comment setCreated_at:[dict objectForKey:#"created_at"]];
[comment setUpdated_at:[dict objectForKey:#"updated_at"]];
[comment setUser:[self dictionaryToUser:[dict objectForKey:#"user"]]];
NSMutableArray *replies = nil;
if ([[dict allKeys] containsObject:#"replies"]) {
replies = [[NSMutableArray alloc] init];
for (NSDictionary *reply in [dict objectForKey:#"replies"]) {
NSLog(#"in");
[replies addObject:[self dictionaryToComment:reply]];
}
}
if (replies != nil) {
[comment setReplies:replies];
NSLog(#"COMMENT REPLIES = %#", comment.replies);
NSLog(#"REPLIES = %#", replies);
[replies release];
}
return [comment autorelease];
}
Console ->
2011-11-30 21:25:14.980 Timbrr[2379:f803] in
2011-11-30 21:25:14.980 Timbrr[2379:f803] COMMENT REPLIES = (null)
2011-11-30 21:25:14.980 Timbrr[2379:f803] REPLIES = (
"<TBComment: 0x68dbeb0>"
)
- (void) setReplies:(NSArray *)_replies {
hasReplies = (_replies == nil ? NO : ([_replies count] == 0 ? NO : YES));
//replies is synthesised
}

After seeing your implementation of setReplies:, I don't think you quite understand how #synthesize works.
#synthesize replies; will generate a getter and a setter for this instance variable. BUT since you're overriding it (and improperly) the synthesized setter is being tossed aside. (In fact, no setter is being created for you at all, since you wrote one yourself.)
The root issue is that in your implementation of setReplies:, you're not actually assigning the value of your replies instance variable to the parameter of the setter.
What I think you want is:
- (void) setReplies:(NSArray *)_replies {
hasReplies = (_replies == nil ? NO : ([_replies count] == 0 ? NO : YES));
// How is your ivar defined in the header file? As _replies, or replies?
if (replies != _replies) {
[replies release];
replies = [_replies retain];
}
}

I would suspect that either comment is nil (though this would require explicit nil-returning code in TBComment, which is possible, but unusual), or that -replies or -setReplies: are incorrectly implemented. Do you have custom implementations for those?
Your implementation of setReplies: never sets _replies.

The replies property is never set - when you define setReplies:, the #synthesize directive does not create any setter method.

Related

Better way than write dozens of empty getters?

I use lazy instantiation on my properties, to have my class created and used as fast as possible. To achieve this, I write lots of 'empty' getters like this:
- (VMPlacesListFilter *)currentFilter
{
if (!_currentFilter) {
_currentFilter = [[VMPlacesListFilter alloc] init];
}
return _currentFilter;
}
They are all the same: if the instance variable is nil, call the -alloc and -init on the class of the property, then return the instance variable. Very common and straightforward.
If I don't create this getter by myself, Objective-C's automatic synthesization creates a getter for me, which does only the returning part (does not init the object if the instance variable is nil).
Is there any way to avoid writing this boilerplate code?
Nope, I'm afraid there's no good way around it, if you really want to have lazy initialization. Personally, I usually save lazy initialization for stuff that could really be time consuming or memory intensive (say, loading images or view controllers), and initialize cheap stuff (like simple data structures or model objects) in init.
- (instancetype) init {
self = [super init];
if( self ) {
_cheapThing1 = [NSMutableArray array];
_cheapThing2 = [[MyModelObject alloc] init];
}
return self;
}
- (ExpensiveThing*) expensiveThing
{
if( _expensiveThing == nil ) {
_expensiveThing = [[ExpensiveThing alloc] init];
}
return _expensiveThing;
}
Unless you're loading something from disk or the network, I wouldn't worry too much about initialization time. Of course, profile it.
I know this is an Objective-C question, but it's worth noting that Swift has lazy initialization built-in.
lazy var currentFilter = VMPlacesListFilter()
First off, I totally agree with #zpasternack that "lazy load" should not be misused. However, automatically generating setters and getters is completely doable with the power of Objective-C runtime. In fact, CoreData is doing this.
Anyway, I have come up with some stupid code implementing a class called LazyClass, in which you can declare dynamic properties like lazyArray (see below). Using dynamic method resolution, when the property is accessed for the first time, a getter that calls the corresponding class's default +alloc and -init method will be automatically added to the class. All underlying instance variables are stored in an NSMutableDictionary called myVars. Of course you can manipulate ivars through the runtime API as well, but using a dictionary should save some work.
Please note that this implementation just shows the basic idea of how it works. It lacks error checking and is not supposed to be shipped.
LazyClass.h
#interface LazyClass : NSObject
#property NSMutableDictionary *myVars;
// lazily initialized property
#property NSArray *lazyArray;
#end
LazyClass.m
#import "LazyClass.h"
#import <objc/objc-runtime.h>
#implementation LazyClass
#dynamic lazyArray;
- (instancetype)init {
self = [super init];
self.myVars = [NSMutableDictionary dictionary];
return self;
}
- (NSMutableDictionary *)getMyVars {
return self.myVars;
}
// the generated getter method
id dynamicGetterMethodIMP(id self, SEL _cmd) {
// selector name, which is also the property name
const char *selName = sel_getName(_cmd);
NSString *selNSName = [NSString stringWithCString:selName encoding:NSUTF8StringEncoding];
NSString *keyPath = [NSString stringWithFormat:#"myVars.%#", selNSName];
if (![self valueForKeyPath:keyPath]) {
// get the actual type of the property
objc_property_t property = class_getProperty([self class], selName);
const char *attr = property_getAttributes(property);
NSString *attrString = [[NSString alloc] initWithCString:attr encoding:NSUTF8StringEncoding];
NSString *typeAttr = [[attrString componentsSeparatedByString:#","] firstObject];
NSString *typeName = [typeAttr substringWithRange:NSMakeRange(3, typeAttr.length - 4)];
// the default initialization
Class typeClass = NSClassFromString(typeName);
[self setValue:[[typeClass alloc] init] forKeyPath:keyPath];
}
return [self valueForKeyPath:keyPath];
}
// the generated setter method
void dynamicSetterMethodIMP(id self, SEL _cmd, id value) {
// get the property name out of selector name
// e.g. setLazyArray: -> lazyArray
NSString *propertyName = NSStringFromSelector(_cmd);
propertyName = [propertyName stringByReplacingOccurrencesOfString:#"set" withString:#""];
propertyName = [propertyName stringByReplacingOccurrencesOfString:#":" withString:#""];
propertyName = [NSString stringWithFormat:#"%#%#", [propertyName substringToIndex:1].lowercaseString, [propertyName substringFromIndex:1]];
NSString *keyPath = [NSString stringWithFormat:#"myVars.%#", propertyName];
[self setValue:value forKeyPath:keyPath];
}
// dynamic method resolution
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) containsString:#"set"]) {
class_addMethod([self class], aSEL, (IMP)dynamicSetterMethodIMP, "^?");
} else {
class_addMethod([self class], aSEL, (IMP)dynamicGetterMethodIMP, "v#:");
}
return YES;
}
#end
Documentation
If it's the verboseness that bothers you, I suppose you could compress lazy initialisers that only need one-line initialization using the ternary operator:
- (VMPlacesListFilter *)currentFilter
{
return _currentFilter ? : (_currentFilter = [[VMPlacesListFilter alloc] init]);
}
DISCLAIMER: I don't do this, but it's interesting that it can be done

primitive accessors in this example

Could someone help me understand the primitive accessors with this example : i don't understand what is automatically set and the order of those methods :
1.after a person is created, is willSave the first method called? (i guess so, because save: is called after we create a person with insertNewObjectForEntityForName )
2.in RootViewController (the second chunk of code), we then call the getter of eyeColor with : person.eyeColor :
a) in eyeColor, we call : [self eyeColorData] ,
b) but setPrimitiveEyeColorData is in willSave, which is accessible only if primitiveEyeColor exists,
c) but setPrimitiveEyeColor is in eyeColor and only called if [self eyeColorData] exists. So, i'm a bit confused with this code, could someone help me?
here's the code about eyeColor and eyeColorData :
#dynamic eyeColorData;
#dynamic eyeColor;
#interface AWPerson (PrimitiveAccessors)
- (UIColor *)primitiveEyeColor;
- (void)setPrimitiveEyeColor:(UIColor *)value;
- (NSData *)primitiveEyeColorData;
- (void)setPrimitiveEyeColorData:(NSData *)value;
#end
+ (id)personInManagedObjectContext:(NSManagedObjectContext *)moc {
return [NSEntityDescription
insertNewObjectForEntityForName:#"Person"
inManagedObjectContext:moc];
}
+ (id)randomPersonInManagedObjectContext:(NSManagedObjectContext *)moc {
AWPerson *randomPerson = [self personInManagedObjectContext:moc];
//...
randomPerson.eyeColor = [self randomColor]; //setter eyeColor
return randomPerson;
}
+ (UIColor *)randomColor {
static NSArray *colorsArray = nil;
if( !colorsArray ) {
colorsArray = [[NSArray alloc] initWithObjects:
[UIColor lightGrayColor],
[UIColor blueColor],
[UIColor greenColor], nil];
}
int randomIndex = arc4random() % [colorsArray count];
return [colorsArray objectAtIndex:randomIndex];
}
- (void)willSave {
UIColor *color = [self primitiveEyeColor];
if( color ) {
[self setPrimitiveEyeColorData:
[NSKeyedArchiver archivedDataWithRootObject:color]];
} else {
[self setPrimitiveEyeColorData:nil];
}
[super willSave];
}
- (UIColor *)eyeColor {
[self willAccessValueForKey:#"eyeColor"];
UIColor *tmpValue = [self primitiveEyeColor];
[self didAccessValueForKey:#"eyeColor"];
if( tmpValue ) return tmpValue;
NSData *colorData = [self eyeColorData];
if( !colorData ) return nil;
tmpValue = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];
[self setPrimitiveEyeColor:tmpValue];
return tmpValue;
}
in RootViewController :
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
AWPerson *person = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[cell setBackgroundColor:person.eyeColor];
}
Thanks
EDIT - Added info on willSave
To answer your first question, willSave is called whenever the object is saved (using the save method). So the first method called will be one of the class methods (used to create the object) or init and then, since you said that the object is saved just after it is created, willSave gets called.
I think the key to understanding this is to realize that eyeColor, primitiveEyeColor, and their setters are all ultimately interacting with the same variable in memory (the iVar named eyeColor). The difference is whether or not the code in the setter/getter (in this case the - (UIColor *)eyeColor { function) is called.
There are just a few different ways to interact with it:
[self primitiveEyeColor]; - This reads the value of the iVar directly.
[self setPrimitiveEyeColor:tmpValue]; - This sets the value of the iVar directly.
[self eyeColor] - This calls the - (UIColor *)eyeColor method in your class (which should ultimately retrieve the iVar or a representation of it).
[self setEyeColor:value] - This calls the - (void)setEyeColor:(UIColor *)newColor method in your class. Note that in this case it doesn't exist so it simply calls the primitive method (and does the KVO magic).
In this particular code, they are using a "non-standard persistent attribute" because NSManagedObject does not support UIColor's. Read about it here.
EDIT 2
To answer your other questions:
a) The color in randomPerson.eyeColor = [self randomColor] is
accessible with [self primitiveEyeColor] (in willSave)?
Yes, once eyeColor is set (either via the setEyeColor method or the setPrimitiveEyeColor method), you can read it from primitiveEyeColor and it will return the same value.
Note that once it is set, eyeColor and primitiveEyeColor return the same value and can be called from anywhere in your class (not just willSave).
b) So if [self primitiveEyeColor] != nil : in eyeColor, the line :
if( tmpValue ) return tmpValue; should therefore always be true...
when can we unarchive eyeColorData if UIColor *tmpValue = [self
primitiveEyeColor] is always returned in -(UIColor *)eyeColor?
This method only looks at eyeColorData (which was stored during the last call to willSave) if eyeColor is nil. This is an optimization because we could skip all of this and just unarchive eyeColorData every time if we wanted to. In this case, once a value is unarchived or set to a new value, it always stores that value and returns it so that we don't have to call unarchive again.
Also, there is really what I believe to be an error here (although it could be by design). Let's say that we perform the following steps:
Set eyeColor to a random color (let's say blue).
save the object.
Set eyeColor to nil
Now, if you check the color using [self eyeColor] it will see that primitiveEyeColor is nil and unarchive eyeColorData again, therefore returning the blue color that was stored previously. You should probably be over-riding the set function so that it sets eyeColorData to nil when eyeColor is set to nil. That way, checking the value of eyeColor after setting it to nil will return nil as expected.

NSMutable Array returning NULL when NSLog

So I have an NSMutable array that I populate in viewDidLoad and then I NSLog it and it returns NULL in the console... I have looked around on stack-overflow and the apple reference and cannot find a solution to my problem.
Heres the code I used:
//Populate array
-(void)populateArray{
[TextArray addObject:#"Text1"];
[TextArray addObject:#"Text2"];
[TextArray addObject:#"Text3"];
[TextArray addObject:#"Text4"];
[TextArray addObject:#"Text5"];
NSLog(#"%#",TextArray);
NSLog(#"%#",[TextArray objectAtIndex:1]);
}
-(void)viewDidLoad
{
TextArray = [[NSMutableArray alloc] init];
[self populateArray];
}
And in the .h file
NSMutableArray *TextArray;
Thanks in advanced!
~Matt
the code you posted does not contain enough information for us to provide a specific answer. chances are, the methods are not being called in the order you expect them (e.g. populateArray is called from another point before viewDidLoad).
some assertions should help diagnose this:
//Populate array
-(void)populateArray
{
assert(nil != self.textArray && "you need to create an array");
[self.textArray addObject:#"Text1"];
[self.textArray addObject:#"Text2"];
[self.textArray addObject:#"Text3"];
[self.textArray addObject:#"Text4"];
[self.textArray addObject:#"Text5"];
assert(5 == [self.textArray count] && "populateArray has been called twice or serious threading error");
NSLog(#"%#",textArray);
NSLog(#"%#",[textArray objectAtIndex:1]);
}
-(void)viewDidLoad
{
assert(nil == self.textArray && "you need to clear your array (e.g. in viewDidUnload) or check if it has already been populated");
self.textArray = [NSMutableArray array];
[self populateArray];
assert(5 == [self.textArray count] && "populateArray has been called twice or serious threading error");
}
lastly, if this array never changes, it's going to be much simpler to create this in your initializer rather than lazily. such an array will not require much memory, in case that was your concern.
Update the construct takes this form:
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
self = [super initWithNibName:nibName bundle:nibBundle];
if (nil != self){
textArray = [[NSArray alloc] initWithObjects:#"Text1", #"Text2", #"Text3", #"Text4", #"Text5", nil];
// ...
}
return self;
}
lastly, viewDidLoad may be certainly be called multiple times on the same instance. the system unloads (then lazily reloads) views in low memory conditions.

Do I need to allocate NSStrings passed in as parameters to a custom initialization method?

Please consider the following two initialization methods.
The first method simply passes the value of the parameters to their respective NSString properties, but the second allocates the properties and then initializes them using the initWithString: method. Is the allocation in the latter example necessary?
Thanks in advance.
-(id)initWithTitle:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = theTitle;
muscleGroup = theMuscleGroup;
equipment = theEquipment;
}
return self;
}
-(id)initWithTitle2:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = [[NSString alloc] initWithString:theTitle];
muscleGroup = [[NSString alloc] initWithString:theMuscleGroup];
equipment = [[NSString alloc] initWithString:theEquipment];
}
return self;
}
The first example is not safe because you are not taking ownership of the strings, so your program will get all crashy if they are later released elsewhere. The second example fixes that problem and will work perfectly well, but is more concisely written thusly:
-(id)initWithTitle2:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = [theTitle copy];
muscleGroup = [theMuscleGroup copy];
equipment = [theEquipment copy];
}
return self;
}
NSString gives you a copy constructor (-initWithString:), which enables you to do what you are doing in #2, but not all classes do. copy requires the class to implement the NSCopying protocol, but is more conformant with the way a Cocoa API developer would expect to be able to copy objects.
Parameter objects don't get copied when you pass them in. So your first example may not always work, it depends how you've initialized your strings.
The following is safer (although remember to release the objects in your dealloc method):
-(id)initWithTitle:(NSString *)theTitle muscleGroup:(NSString *)theMuscleGroup equipment:(NSString *)theEquipment {
if((self = [super init])){
title = [theTitle retain];
muscleGroup = [theMuscleGroup retain];
equipment = [theEquipment retain];
}
return self;
}
Example 1 will assign the pointers. It makes no attempt to retain the objects and is vulnerable to something outside changing the content of the objects.
It could work depending on how the arguments are constructed in the first place;
Example 2 will copy the string objects and retain them. As long as you release in the dealloc then its the preferable method.
FWIW
title = [theTitle copy];
or
title = [[NSString stringWithString:theTitle] retain];
are equally good in Ex 2

When to use self and when to use retain

am working my way through the "Beginning iPad Development" Apress book and have noticed that sometimes when the author is assigning values to a property they will use:
self.variable = value;
and other times, they will use:
variable = [value retain];
In both cases variable is a property defined as:
#property (nonatomic, retain) TYPE variable;
I wondered if anyone knew why this is done to help me better understand
Thanks,
William
One place where you use the second form is if you're defining your own setter method. You can't assign to self.variable there, because you'll call the setter recursively. So for example this is wrong:
-(void)setVariable:(TYPE*)value {
if (value != variable) {
[variable release];
self.variable = [value retain]; // WRONG! Calls
// [self setVariable:[value retain]]
}
}
This is right:
-(void)setVariable:(TYPE*)value {
if (value != variable) {
[variable release];
variable = [value retain];
}
}
Does that help?
They are often equivalent memory-wise. The compiler turns self.variable = value into [self setVariable:value] which then calls the generated setter (if you're using #synthesize) and retains it for you.