I have read Apple's Blocks Programming Topics and my due diligence searching online, but I am still unclear if I am implementing my blocks correctly. I have an array of clients as a property that is populated when an NSNotification is sent. Clients is used as a tableview data source. The code below works, but I am curious if it is putting self in a retaining cycle. Should I do something like __block id theClients = self.clients; and then reference theClients inside the block?
#property (strong, nonatomic) NSMutableArray *clients;
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__block id observer = [notifyCenter addObserverForName:queryHash
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification* notification){
// Explore notification
if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
if (self.clients)
{
self.clients = nil;
}
self.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
for (NSDictionary *row in rows) {
[self.clients addObject:row];
}
} else {
NSLog(#"CLIENTS ERROR Returned: %#",[notification.userInfo objectForKey:kerrorReturnKey]);
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
There is no problem in accessing the clients property because it is a strong (i.e. retained) property. So you don't need the __block here.
One problem can be that self might not exist anymore when the notification is sent. Then you would access the deallocated object and the app can crash! To avoid that you should remove the observer in the dealloc method.
The __block before id observer is definitely required !
EDIT:
In iOS 5 you can safely capture self using a weak reference:
__weak id weakSelf = self;
Then inside the block you can safely use weakSelf.clients. The variable weakSelf will turn into nil automatically when the object is deallocated.
Yes, you have a retain cycle, at least until the notification occurs. When you access the clients ivar in the block, the block will retain self. It will be retained by the block in notification center until the notification occurs (since you remove the observer at the end of the block). If that's not desirable in your case, you can use a weak reference to self.
NSNotificationCenter *notifyCenter = [NSNotificationCenter defaultCenter];
__weak id weakSelf = self;
id observer = [notifyCenter addObserverForName:queryHash
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification* notification) {
if (weakSelf) {
if ([[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0]) {
NSArray *rows = [[notification.userInfo objectForKey:kdatasetReturnKey] objectAtIndex:0];
if (weakSelf.clients)
{
weakSelf.clients = nil;
}
weakSelf.clients = [[NSMutableArray alloc] initWithCapacity:rows.count];
for (NSDictionary *row in rows) {
[weakSelf.clients addObject:row];
}
} else {
NSLog(#"CLIENTS ERROR Returned: %#",[notification.userInfo objectForKey:kerrorReturnKey]);
}
}
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
I don't see any reason you need to __block qualify observer.
It's also not clear you're getting anything out of using the block-based API here. If you don't want to worry about the potential retain cycle, you could just use addObserver:selector:name:object: and put the body of your notification callback in an instance method.
Related
I was trying to prepare a simple demo for using NSNotificationCenter when I got unexpected crashes, but only sometimes. I have two classes, Scout and Shouter. On -init, Shouter posts a notification to NSNotificationCenter, and Scout adds itself as an observer for the same notification on its respective -init.
When the application crashes, it has "unrecognized selector sent to instance 0x100109480" (address changes of course). This led me to check the addresses of the objects to see what happens. It appears that sometimes a Shouter init after a Scout init is overwriting the address for the Scout object.
Code is below. I'm hoping to understand why this happens. I've tried removing the id/idCounter mechanism and using a hard-coded int in case my understanding of static was wrong.
Relevant code for Shouter:
#implementation Shouter{
int _id; //An ID-code to recognize different instances of Shouter.
}
-(id)init{
NSLog(#"init test Shouter - what is self? %#", self);
if(self=[super init]){
static int _idCounter = 0;
_id = _idCounter++;
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:_id] forKey:#"ID-code"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"sample" object:nil userInfo:dictionary];
}
return self;
}
#end
Relevant code for Scout:
#implementation Scout
-(id)init{
NSLog(#"init test Scout - what is self? %#", self);
if(self=[super init]){
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(messageReceived:)
name:#"sample"
object:nil];
}
return self;
}
- (void) messageReceived:(NSNotification*)notification{
NSDictionary *dictionary = [notification userInfo];
id object = [dictionary objectForKey:#"ID-code"];
int code = -1;
if([object isKindOfClass:[NSNumber class]]){
code = [((NSNumber*)object) intValue];
}
NSLog(#"Received a message from a Shouter instance. ID-code was: %i", code);
}
#end
Relevant code for main:
int main(int argc, const char * argv[])
{
#autoreleasepool {
[[Shouter alloc] init];
[[Scout alloc] init];
[[Shouter alloc] init];
}
return 0;
}
Example output in crash instance:
2014-01-23 11:38:30.800 Lab 3 Snippets[3461:303] init test Shouter - what is self? <Shouter: 0x100109480>
2014-01-23 11:38:30.802 Lab 3 Snippets[3461:303] init test Scout - what is self? <Scout: 0x1003007b0>
2014-01-23 11:38:30.803 Lab 3 Snippets[3461:303] init test Shouter - what is self? <Shouter: 0x1003007b0>
2014-01-23 11:38:30.803 Lab 3 Snippets[3461:303] -[Shouter messageReceived:]: unrecognized selector sent to instance 0x1003007b0
You should always unregister notifications when your object is deallocated, e.g:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"sample" object:nil];
[super dealloc]; // Skip this if you're using ARC
}
What might happen is that you're Scout object is deallocated right after its init method finishes, and the next Shooter object is allocated at the same address. NSNotificationCenter doesn't know this has happened, and tries to call messageReceived: on the former Scout object. Instead it finds a Shooter object that does not respond to the method, hence your error.
NSNotificationCenter never retains objects registered as observers, because that would create a lot of retain cycles that would be really hard to work around. Make sure something holds on to your objects for as long as you need them to exist.
In guessing ARC is on. ARC will see that the object is never used again and release it immediately, but since you never unregister the notification subscription, the notification center tries to talk to the new object that's allocated in its place.
You can also use [[NSNotificationCenter defaultCenter] removeObserver:self];on dealloc method it will remove all references of self on the Notification Center.
I finally found my memory bug is caused by referring self strongly in a block. But I don't know why in a similar case, the weak is not needed:
I have a CameraCaptureManager class doing image capture tasks, and a CameraViewController has a strong property of this manager. The manager has weak delegate property pointing back to the controller.
This is where I must use weakSelf in the manager, otherwise -(void)dealloc won't be called:
// in CameraCaptureManager
__weak CameraCaptureManager *weakSelf = self;
void (^deviceOrientationDidChangeBlock)(NSNotification *) = ^(NSNotification *notification) {
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
[weakSelf updateVideoOrientation:deviceOrientation];
};
self.deviceOrientationDidChangeObserver = [notificationCenter addObserverForName:UIDeviceOrientationDidChangeNotification
object:nil
queue:nil
usingBlock:deviceOrientationDidChangeBlock];
The manager holds the deviceOrientationDidChangeObserver strongly, so weakSelf is needed to break the memory retain cycle. That's fine, I got that... but I find I don't have use weakSelf in a similar case in the same class:
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error){
UIImage *image = nil;
if (imageDataSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
image = [[UIImage alloc] initWithData:imageData];
}
if ([self.delegate respondsToSelector:#selector(captureManager:capturedStillImage:)]) {
[self.delegate captureManager:weakSelf capturedStillImage:image];
}
}];
The manager also holds the stillImageOutput strongly, but why I can use the strong "self" in the completion block? The manager object gets dealloc with this strong self inside the block. I'm confused, please shed some light.
Also do I need to use weakSelf in the 2nd case even when it won't cause any retain cycle?
In your second code example you have a temporary retain cycle. When the completionHandler block has been called, the block is released and with it the captured self, so
that the release cycle is broken.
as i told here i am using NSNotificationCenter .
on class A (observer) on the init method i have got :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getSensorsData:) name:#"HotSpotTouched" object:nil];
on classB i have got :
//FILL NSDICTIONARY WITH DATA
[dict setObject:#"SPOT1" forKey:[array objectAtIndex:0]];
[dict setObject:#"SPOT2" forKey:[array objectAtIndex:1]];
[dict setObject:#"SPOT3" forKey:[array objectAtIndex:2]];
[dict setObject:#"SPOT4" forKey:[array objectAtIndex:3]];
[dict setObject:#"SPOT5" forKey:[array objectAtIndex:4]];
[[NSNotificationCenter defaultCenter] postNotificationName:#"HotSpotTouched" object:dict];
the function in class A getSensorsData is not being called.
whats wrong here ??
thanks !
You are passing in dict as notificationSender when posting the notification, and nil when you add the observer. This way your notification is filtered out because the senders mismatch.
Update:
As pointed out by joerick in the comments, passing nil when adding an observer will disable sender filtering. So this isn't the problem here.
I just created a small sample project and for me notifications are delivered.
#Rant: If you want to pass arbitrary data along with your notification, you should use the userInfo dictionary (as pointed out by Cyrille in the comment).
The calls to the notification center look correct. I suspect the problem is due to the liffe cycle of object A. You say that you're registering for the notification in the init method. Have you correctly assigned self?:
-(id)init
{
//self does not have a meaningful value prior to the call to [super init]
self = [super init];
if (self != nil)
{
//ensure addObserver is called in the if code block
}
return self;
}
Also, it's good practice to use constants for notification names as they mitigate against typos. See Constants in Objective C.
problem solved:
if you passing a null argument, the observer is not getting the call !
my NSDictionary argument was null( because a reason i still dont know), so the call is not fired.
Please look at the code below and suggest the best approach. I can't quite tell whether the code is correct. When adding objects to arrays, do they get a retain count? In the second function, am I releasing the local variable "mySubview" or the original object?
// this is a class property
myArray = [[NSMutableArray alloc] init];
- (void)createSubview
{
UIView *mySubview = [[UIView alloc] init];
[self addSubview:mySubview];
[myArray addObject:mySubview];
}
-(void)eventHandler:(NSNotification *) notification
{
UIView *mySubview = [notification object];
[myArray removeObjectIdenticalTo:mySubview];
[mySubview removeFromSuperview];
[mySubview release];
}
When adding objects to arrays, do they
get a retain count?
Yes.
In the second function, am I releasing
the local variable "mySubview" or the
original object?
UIView *mySubview;' defines a local variable, mySubview, which is a pointer to -- a reference to -- an instance of the UIView class. There is no such thing as a "local object" or "stack object" in Objective-C (save for blocks, but that is beyond the scope of this question).
So, no, when you call [mySubview release] you are sending -release to the instance of of UIView included in notification.
That release is balancing the retain implied by the alloc. Which isn't the right pattern at all. You should do something like:
- (void)createSubview
{
UIView *mySubview = [[UIView alloc] init];
[self addSubview:mySubview];
[myArray addObject:mySubview];
[mySubview release];
}
-(void)eventHandler:(NSNotification *) notification
{
UIView *mySubview = [notification object];
[myArray removeObjectIdenticalTo:mySubview];
[mySubview removeFromSuperview];
}
Oh, by "class property", I'm assuming you mean "instance variable"?
Given the following property definition:
#property (nonatomic,retain) MyObject* foo;
does the following code cause a memory leak:
self.foo = [[MyObject alloc] init];
?
It looks like the alloc call increments the retain count on the object to 1, then the retain inside the property setter increases it to 1. But since the initial count is never decremented to 0, the object will stick around even when self is released. Is that analysis correct?
If so, it looks like I have two alternatives:
self.foo = [[[MyObject alloc] init] autorelease];
which is not recommended on the iPhone for performance reasons, or:
MyObject* x = [[MyObject alloc] init];
self.foo = x
[x release];
which is a bit cumbersome. Are there other alternatives?
Are there any alternatives?
No.
You are not going to be able write much of an iPhone application without using autorelease and the Cocoa Touch library uses them in many places. Understand what it's doing (adding the pointer to a list for removal on the next frame) and avoid using it in tight loops.
You can use class method on MyObject that does alloc/init/autorelease for you to clean it up.
+ (MyObject *)object {
return [[[MyObject alloc] init] autorelease];
}
self.foo = [MyObject object];
The easiest way to manage a retained property on the iPhone is the following (autorelease is not as bad as you think, at least for most uses):
-(id)init {
if (self = [super init]) {
self.someObject = [[[Object alloc] init] autorelease];
}
return self;
}
-(void)dealloc {
[someObject release];
[super dealloc];
}
The autorelease releases the reference to the floating instance which is assigned to self.object which retains its own reference, leaving you with the one reference you need (someObject). Then when the class is destroyed the only remaining reference is released, destroying the object.
As described in another answer, you can also create one or more "constructor" messages to create and autorelease the objects with optional parameters.
+(Object)object;
+(Object)objectWithCount:(int)count;
+(Object)objectFromFile:(NSString *)path;
One could define these as:
// No need to release o if fails because its already autoreleased
+(Object)objectFromFile:(NSString *)path {
Object *o = [[[Object alloc] init] autorelease];
if (![o loadFromFile:path]) {
return nil;
}
return o;
}
You are right, self.foo = [[MyObject alloc] init]; is leaking memory. Both alternatives are correct and can be used. Regarding the autorelease in such a statement: keep in mind that the object will released by the autorelease pool as soon as the current run loop ends, but it will most probably be retained a lot longer by self, so there is no issue with memory usage spikes here.