Using delegates in OS X - macos

I'm new at OS X development, I've been having a problem getting a delegate callback and I somehow suspect that it might be a memory problem. I have an NSViewController. In it's init method I am setting up a custom NSObject as so:
MyObject *aManager = [[MyObject alloc] initManager];
__theManager = aManager;
self.theManager.delegate = self;
[aManager release];
the delegate I've setup as nonatomic, assign. Looking at the breakpoints I should be seeing the callback in my view controller but this never happens. Any ideas?

__theManager = aManager; should be self.theManager = aManager;, assuming theManager is a retained property. The problem you have is that alloc] init]; gives aManager a retain count of +1. __theManager = aManager; does not increase that count, as the iVar is set directly. When you release it, the retain count becomes 0, and so it is deallocated.

Related

KVO versus NSUndoManager

I have a property on my document class called passDescription, of type NSMutableDictionary. When I call setPassDescription:, the current value of the property is archived into an NSData instance using NSJSONSerialization. The property’s backing ivar, passDescription, is updated, and then an undo action is registered. The selector invoked by the action reconstitutes the NSData given to it and calls setPassDescription:.
Now, here’s the joker: passDescription is being observed using Key-Value Observing. Considerable experimentation and examination in Xcode’s debugger reveals that the old value and the new value are identical. (I know that this isn’t a pointer-aliasing issue, as that’s why I’m using an NSData instance. The NSData is created before I record the new value, making it independent of what it was created from.) Thus, when I press Command-Z to undo, nothing happens as the value that has just been restored is no different from the value that has been overwritten by the undo.
The only thing I can think of that may be causing this is that KVO is setting the passDescription ivar for me, before setPassDescription: gets called. Why would this be, and how could I prevent KVO from doing that? (I have confirmed that the setter isn’t being called twice. If it was, I would see double output in the debugger console.)
Here is the source for setPassDescription:.
- (void)setPassDescription:(NSDictionary *)param
{
NSLog(#"passDescription (before) = \n%#", passDescription);
NSError *error;
NSData *archivedOldValue = [NSJSONSerialization dataWithJSONObject:passDescription options:0 error:&error];
NSAssert(archivedOldValue != nil, #"Could not archive old pass description: %#", error);
NSData *blob = [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:&error];
if (blob == nil) #throw [NSException exceptionWithName:#"PBJSONException" reason:#"Could not serialize pass.json" userInfo:#{ #"error": error }];
[self.fileBrowserRoot fileWrapperWithName:#"pass.json"].fileContents = blob;
[passDescriptionLock lock];
[[self.undoManager prepareWithInvocationTarget:self] setPassDescriptionFromData:archivedOldValue];
passDescription = param;
[passDescriptionLock unlock];
NSLog(#"passDescription (after) = \n%#", passDescription);
// After the pass description has been set, refresh the list of build issues.
[self recheckForIssues];
}

Setting a weak ivar and getting nil

In an ARC project, I'm setting a __weak ivar (declared as id __weak weakLayer;) to a CALayer which is retained by it's superlayer. Most times this works. Sometimes, the weak ivar tests as nil. I wrote some test code to simplify debugging that demonstrates the problem. I expect this to be an infinite loop but it breaks out, usually in less than 20 iterations. The number of iterations required to break out is not consistent.
array = [NSMutableArray array];
while (1) {
CALayer *layer = [CALayer layer];
[array addObject:layer];
weakLayer = layer;
if (!weakLayer) {
NSLog (#"nil");
break;
}
NSLog(#"not nil");
}
If I drop a breakpoint with an action of po weakLayer inside the if, it prints a valid object. I have tested on Xcode 4.3.3 and 4.4 with the Lion and Mountain Lion SDKs.
3 people (including myself) have now confirmed this to work on 10.8, but not on 10.7.4.

analyze and memory alerts in xcode

I ran 'analyze" in xcode on a current iOS project to try to track down a freeze issue and there are a number of memory alerts that I don't understand (screenshot of one below).
What is going on there: I have a custom ObjC class extending NSObject; in the init method I alloc/init an NSMutableArray and then in a loop, populate it with NSMutableArrays. This nested array is declared as a property and released in dealloc(). It lives for the life of the app.
Am I doing this wrong? I don't understand the alert#3: # object not referenced in this execution path and has a retain count of +1.
Since my class allocs the outer array, it owns it and will clean it up. Do the inner arrays need to be released?
Thanks for any tips - still new at this.
EDIT/ADDITION
Trying to stamp out the additional memory warnings I am getting so I thought I would add to the question here in the event someone stumbles upon this w/ the same issue.
I am getting the following alert with the code below (the 2nd line "[keyArray addObject: etc"). What is going on: I have a custom class (Key - based on NSObject) that I instance and store in an array. Based on answers to my previous question, I guess my alloc increases the retain count and then when it is added to the array, the retain count isn't decremented - so the memory warning occurs.
What is the proper way to handle something like this? Use a placeholder like this:
Key * k = [[Key alloc] initKeyWithPath:path isBlackKey:NO]];
[keyArray addObject: k];
[k release];
Is that the proper way to do it? Or is there I way to write the custom class to return an autoreleased obj? (thanks and sorry to be so long winded!).
Potential leak of an object allocated on line 460
Method returns an Objective-C object with a +1 retain count (owning reference)
Object allocated on line 460 is not referenced later in this execution path and has a retain count of +1 (object leaked)
-(void) addOctaveToArraysWithTransform:(CGAffineTransform*)trans andPath: (CGMutablePathRef) path
{
path = [self createCF_keyUsingTransform: trans];
[keyArray addObject:[[Key alloc] initKeyWithPath:path isBlackKey:NO]];
}
Key.h
#import <Foundation/Foundation.h>
#import "Key.h"
#interface Key : NSObject {
#public
CGMutablePathRef keyPath;
BOOL isBlackKey;
NSValue * path;
int keyState;
BOOL needsRedraw;
}
#property (nonatomic, assign) int keyState;
#property (nonatomic, assign) BOOL needsRedraw;
#property (nonatomic) CGMutablePathRef keyPath;
-(id) initKeyWithPath:(CGMutablePathRef) aPath isBlackKey:(BOOL)flag;
-(CGMutablePathRef) getKeyPath;
#end
Key.m
#import "Key.h"
#implementation Key
#synthesize keyState, needsRedraw, keyPath;
-(id) initKeyWithPath:(CGMutablePathRef) aPath isBlackKey:(BOOL)flag
{
if ((self = [super init])){
isBlackKey = flag;
keyState = 0;
needsRedraw = NO;
keyPath = aPath;
CGPathRetain(keyPath);
}
return self;
}
-(CGMutablePathRef) getKeyPath
{
return keyPath;
}
#end
Yes, you have to release the inner arrays to balance the alloc/init. Remember the outer array will retain each inner array, and the outer array will presumably release those later. But here you are still responsible for the alloc/init you just did.
Hope that helps.
You have an allocation of an NSMutableArray on each iteration of the for-loop. Instead use: NSMutableArray array] which is a convenience method that return an autoreleased NSMUtableArray suitable for adding to fieldNotes which will retain the NSMutableArray.

componentsJoinedByString gives me EXC_BAD_ACCESS

I have an NSMutableArray i am trying to convert into a string.
Declaring my NSMutableArray...
NSMutableArray *listData;
And later inside a method...
NSString *foo = [listData componentsJoinedByString:#"|"];
NSLog(#"%#",foo);
It seems no matter what i try i keep getting EXC_BAD_ACCESS.
To make sure each element in my array was an NSString i also tried this...
NSMutableArray *mArray = [[NSMutableArray alloc] init];
for (id ln in listData) {
NSString *boo = [NSString stringWithFormat: #"%#",ln];
[mArray addObject:boo];
}
NSString *foo = [mArray componentsJoinedByString:#"|"];
NSLog(#"%#",foo);
I can manipulate my NSMutableArray by adding/deleting objects in the same method or other methods inside my class. But when i try "componentsJoinedByString" the error pops up. Does anyone have any advice or another way i can combine this array into a single NSString?
In the code you've given, there will never be an NSMutableArray for listData. At some point in your code, you'll need to create one, and presumably populate it.
Edit
Okay, so you may get into memory management problems here, so let's be a bit clearer:
You're synthesizing getters and setters for the instance variable, so it's good practice to use those to access it, they'll take care of retain and releasing appropriately.
To set listData you can simply use
self.listData = [listManage getList:[[NSUserDefaults standardUserDefaults] stringForKey:#"list_name"] list:#"LIST"];
or
[self setListData:[listManage getList:[[NSUserDefaults standardUserDefaults] stringForKey:#"list_name"] list:#"LIST"]];
if you prefer.

What is the better way of handling temporary strings?

I have a situation where I need to use some strings temporarily but I've read so many conflicting things that I'm a bit confused as to what the best way to proceed is.
I need to assign some strings inside an if structure but use them outside the if structure so they need to be created outside the if, I was thinking something like:
NSString *arbString = [[NSString alloc] init];
if(whatever)
{
arbString = #"Whatever"
}
else
{
arbString = #"SomethingElse"
}
myLabel.text = arbString;
[arbString release];
I have seen examples where people just used:
NSString *arbString;
to create the string variable
Google's Objective C guide says it's preferred to autorelease at creation time:
"When creating new temporary objects, autorelease them on the same line as you create them rather than a separate release later in the same method":
// AVOID (unless you have a compelling performance reason)
MyController* controller = [[MyController alloc] init];
// ... code here that might return ...
[controller release];
// BETTER
MyController* controller = [[[MyController alloc] init] autorelease];
So I have no idea, which is the best practice?
In the example you posted, you actually lose the reference to the NSString you created when you assign it in arbString = #"Whatever". You then release the string constant (which is unreleasable, by the way).
So there's a memory leak, since you never release the NSString you created.
Remember that all these types are pointers, so = only reassigns them.
As for the question, in this example, you don't need the [[NSString alloc] init]. You don't need to copy the string into a local variable anyway, you can just set myLabel.text to the string constant #"Whatever".
(edit: that's not to say that you can't use your pointer arbString, arbString = #"Whatever"; myLabel.text = arbString is fine. But this is just pointer assignment, not copying)
If you needed to manipulate the string before you returned it, you would create an NSMutableString, and either release or auto-release it. Personally, create autoreleased objects using class methods, so in this example, I'd use [NSString string], or [NSString stringWithString:], which return autoreleased objects.

Resources