arc and no-arc issues - ios

This is my code
__weak KDObject *obj = [KDObject fetchObj] ;
NSLog(#"%#", obj) ; // I think it should be nil, but it is not
obj.i = 10 ;
NSLog(#"%d", obj.i) ;
In KDObject.m
#implementation KDObject
+ (instancetype)fetchObj
{
return [[self alloc] init] ;
}
#end
the result is the same whatever KDOjbect.m is compile with -fno-objc-arc flag or without -fno-objc-arc flag
Anybody has ideas why obj is not nil ?

Related to your Q and to your answer:
-fectchObject is a method not belonging to any method family with ownership transfer. Therefore ARC has to ensure that returning the reference is safe. That means that losing the strong reference in the local scope of -fetchObject does not give up the last reference.
One way to accomplish this is to use the autorelease pool. But ARC does not guarantee that the ARP is used. Moreover it tries not to use the ARP, because it is the solution with the highest memory pressure.
So the things happening depends of the compiler implementation, attributes set to the method and what the compiler sees in source code (esp. implementation of -fetchObject). So you should not rely on returning in ARP.
__weak is guaranteed to be nil, if the object is destroyed. But it is not guaranteed that the object is destroyed in the earliest possible moment. This is subject of optimization.

From the docs about __weak
__weak specifies a reference that does not keep the referenced object alive. A weak reference is set to nil when there are no strong
references to the object.
Whether it's __weak or not KDObject *o = [[KDObject alloc] init] creates an object so o is not nil.
__weak is something realated to memory management. If none of strong objects are pointing to a weak object, it would be released from memory.

- (void)loadView
{
[super loadView];
TestObject *obj_ = [[TestObject alloc] init];
pObj = obj_;
if(pObj == nil)
{
NSLog(#"pObj_ is not nil");
}
__weak TestObject *obj2_ = [[TestObject alloc] init];
if(obj2_ == nil)
{
NSLog(#"obj2_ is nil");
}
__weak TestObject *obj3_ = [TestObject createInstance];
if(obj3_ == nil)
{
NSLog(#"obj3_ is nil");
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(pObj == nil)
{
NSLog(#"pObj is nil");
}
}
KudoCC requested this code. I used LLVM5.1. If I use -fno-objc-arc to TestObject.h, the objc3_ became not nil.

Related

Difference between self.completionBlock = ^{} and (void)(^completionBlock)(void) = ^{}

Recently after following Apple documentation
I used the following conventions to avoid retain cycle issues.
__weak __typeof(self) weak_self = self;
void(^completionBlock)(void) = ^(){
__typeof(self) strong_self = weak_self;
if (strong_self) {
if (strong_self->_completion != NULL) {
strong_self->_completion();
}
}
};
But this code is found to be crashing because self is getting deallocated before invoking the block.
When I used the following it is found to be working.
__block __typeof(self) block_self = self;
void(^completionBlock)(void) = ^(){
if (block_self->_completion != NULL) {
block_self->_completion();
}
};
Now I am confused when we should use __weak reference. Only in the following case of "self.completionBlock"
__weak __typeof(self) weak_self = self;
self.completionBlock = ^(){
if (weak_self->_completion != NULL) {
weak_self->_completion();
}
};
Any light on this conditions will be very useful to me.
My implementation code is given below.
=================================================
File MyViewController
#implementation MyViewController
//starting point
- (void)myPushMethod {
__weak __typeof(self) weak_self = self;
MyViewControllerTransitioning *delegateObj = [[MyViewControllerTransitioning alloc] initWithCompletion:^{
//resetting the delegate
__typeof(self) strong_self = weak_self;
if (strong_self) {
strong_self.navigationController.delegate = nil;
}
}];
self.navigationController.delegate = delegateObj;
[self.navigationController pushViewController:someViewController animated:_animated];
//it is found that delegateObj is getting deallocated while reaches this point
}
#end
=================================================
File MyViewControllerTransitioning
#implementation MyViewControllerTransitioning
- (instancetype)initWithCompletion:(completionHandler)completionHandler
{
if(self = [super init])
{
if (completionHandler != NULL) {
_completion = completionHandler;
}
}
return self;
}
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
id<UIViewControllerAnimatedTransitioning> animationTransitioning = nil;
//crashes here
__block __typeof(self) block_self = self;
void(^completionBlock)(void) = ^(){
if (block_self->_completion != NULL) {
block_self->_completion();
}
};
//showing presentation-up animation if Push
if (operation == UINavigationControllerOperationPush) {
animationTransitioning = [[MyAnimator alloc] initWithAnimationCompletion:completionBlock];
}
return animationTransitioning;
}
- (void)dealloc{
//dealloc is called before invoking completionBlock
}
#end
=================================================
File MyAnimator
#implementation MyAnimator
- (instancetype)initWithAnimationCompletion:(presentationUpCompletion)completionHandler
{
if(self = [super init])
{
if (completionHandler != NULL) {
_completion = completionHandler;
}
}
return self;
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return my_duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
//animation logic
[UIView animateWithDuration:duration animations: ^{
//animation logic
} completion: ^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)animationEnded:(BOOL) transitionCompleted {
if (transitionCompleted && _completion != NULL) {
_completion();
}
}
#end
In my original answer, below, I walk through the standard weakSelf pattern (and the weakSelf-strongSelf "dance"). My conclusion was that presentation slide you reference is absolutely correct regarding the weakSelf pattern (though that presentation is stylistically dated).
You subsequently provided a more complete code sample, and it turns out that it suffers from a different, unrelated problem. (Worse, it was a problem that only manifests itself when the strong reference cycles are resolved. lol.) Bottom line, the code sets the delegate of the navigation controller to a local object which falls out of scope. Because the navigation controller doesn't retain its delegate, you were ending up with a dangling pointer to this deallocated object.
If you keep your own strong reference to this delegate object (keeping it from being deallocated), the problem goes away.
My original answer is below.
You said:
used the following conventions to avoid retain cycle issues.
__weak __typeof(self) weak_self = self;
void(^completionBlock)(void) = ^(){
__typeof(self) strong_self = weak_self;
if (strong_self) {
if (strong_self->_completion != NULL) {
strong_self->_completion();
}
}
};
But this code is found to be crashing because self is getting deallocated before invoking the block.
No, this is a very common pattern (often jokingly called the "weak self, strong self dance"). There is nothing wrong with this. If it's crashing, it's for other reasons.
Sure, I'd use modern naming conventions (weakSelf and strongSelf rather than weak_self and strong_self). And I'd remove the __ at the start of __typeof. And I wouldn't be inclined to dereference and ivar, but rather use a property. So, I might end up with something like:
__weak typeof(self) weakSelf = self;
void(^completionBlock)(void) = ^(){
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
if (strongSelf.completion != NULL) {
strongSelf.completion();
}
}
};
But, still, if that is crashing, you have some other problem. Frankly, you have a block calling a block so it's a little hard to guess where your problem rests, but the problem is not in the "weak self" pattern.
Later you go on to suggest using:
__block __typeof(self) block_self = self;
That does not do what you think it does. The goal of the "weak self" pattern is to break the strong reference cycle (formerly known as retain cycle), but in ARC, this __block reference does nothing to resolve that strong reference cycle. Note, in non-ARC code, this block_self pattern was used to break retain cycles, but it will not do the job in ARC.
Finally, you go on to suggest:
__weak __typeof(self) weak_self = self;
self.completionBlock = ^(){
if (weak_self->_completion != NULL) {
weak_self->_completion();
}
};
This has two serious problems. First, you are dereferencing a weak variable. If weak_self was nil, it would crash. Second, even if you fixed that by using property accessor methods rather than dereferencing ivars, you have another problem here, namely a race condition.
Bottom line, the "weak self" pattern from that presentation is correct. Likewise, the "weak self, strong self dance" from your first example is also correct. If it's crashing for you, you have to provide us a MCVE which reproduces the problem. But in ARC code, this is a perfectly valid pattern.
self.completionBlock
self has a strong reference to completionBlock (it's a property variable), then u need to weak reference to self to avoid the cycle.
(void)(^completionBlock)(void) = ^{}
self has NO strong reference to completionBlock (it's a local variable), no need to create a weak reference to self.
And the reason for the one you added __block works is that "__block causes the variable to be strongly referenced".

ARC two-way relationship

So I want to have multiple nodes that are connected. Every node has outgoing and incoming connections. But the NSMutableArrays are creating leaks although i'm using ARC. How can i get the objects to be released properly? I'm already using an autoreleasepool.
The code so far is:
#interface TestObj()
#property(strong) NSMutableArray *incoming;
#property(strong) NSMutableArray *outgoing;
#end
#implementation TestObj
#synthesize incoming,outgoing;
- (id)init
{
self = [super init];
if (self) {
incoming = [NSMutableArray array];
outgoing = [NSMutableArray array];
}
return self;
}
-(void)addIncoming:(TestObj *)incomingN {
if([incoming indexOfObject:incomingN] == NSNotFound) {
[incoming addObject:incomingN];
[incomingN addOutgoing:self];
}
}
-(void)addOutgoing:(TestObj *)outgoingN {
if([outgoing indexOfObject:outgoingN] == NSNotFound) {
[outgoing addObject:outgoingN];
[outgoingN addIncoming:self];
}
}
With ARC, as with manual memory management on iOS, you need to avoid retain cycles. If you have one object that is retaining a second, and the second is retaining the first, those two will never be deallocated even if nothing else references them, so you have a memory leak.
You need to make it so that you aren't referencing them like this. NSArray and NSMutableArray keep strong references to other objects. You can do something like the following to create a weak reference that you can story in the array:
NSValue *val = [NSValue valueWithNonretainedObject:object];
If you store val in the array, it won't have a strong reference to the object, so it can be deallocated. However, you have to be careful that you aren't creating a situation where some of your objects have no strong references, or they will get deallocated before you want them to.
Well this may sound basic, but have tried setting them = to nil?

Memory management and properties (init/dealloc)

Until yesterday I thought I understood how properties memory management works, but then I ran an "Analize" task with XCode and got plenty of "This object is not own here". Here is a simple example that describes my problem :
MyObservingObject.h:
#interface MyObservingObject : NSObject
#property(nonatomic, retain) NSMutableDictionary *observedDictionary;
-(id)initWithDictCapacity:(int)capacity;
#end
MyObservingObject.m:
#synthesize observedDictionary;
-(id)initWithDictCapacity:(int)capacity {
self = [super init];
if (self) {
self.observedDictionary = [[[NSMutableDictionary alloc] initWithCapacity:capacity] autorelease];
}
return self;
}
- (void)dealloc {
// The following line makes the Analize action say :
// "Incorrect decrement of the reference count of an object that is not owned at this point by the caller"
[self.observedDictionary release], self.observedDictionary=nil;
[super dealloc];
}
What I don't understand is Why should I leave this property without calling release on it? My #property is set as retain (copy does the same), so when I'm doing self.myRetainProperty = X, then X got its retain count increased (it's owned by self), didn't it ?
You should let the setter do the releasing for you, so remove the call to release in dealloc:
- (void)dealloc {
self.observedDictionary=nil;
[super dealloc];
}
This is because the setter will be synthensized to something like:
- (void)setObject:(id)object
{
[object retain];
[_object release];
_object = object;
}
Which will work as desired when you pass in nil.
It did get increased, but when you set it to nil, the setter method first releases the backing instance variable, and only then does it retain and assign the new value. Thus setting the property to nil is enough, setting the ivar to nil leaks memory, though.
For your better understanding: the typical implementation of an autogenerated retaining setter is equivalent to something like
- (void)setFoo:(id)foo
{
if (_foo != foo) {
[_foo release];
_foo = [foo retain];
}
}
Also note that, as a consequence, you should never release properties like this. If you do so, the backing ivar may be deallocated, and messaging it (release by the accessor when setting the property to nil afterwards) can crash.
You don't need to do
[self.observedDictionary release]
before
self.observedDictionary=nil;
This is enough, because this is a property, and it will automatically send release to previous value
self.observedDictionary=nil;
The reason for the compiler warning is because of the way you are retrieving the object.
By calling
[self.observedDictionary release];
you are in fact going through the accessor method defined as
- (NSDictionary *)observedDictionary;
This returns your object but due to the naming of observedDictionary the compiler assumes that there is no transfer of ownership e.g. the callee will not have to release this object unless they take a further retain. It is because of this that the compiler thinks you are going to do an overrelease by releasing an object that you don't actually own.
More specifically the convention for method names that transfer ownership is for them to start with copy, mutableCopy, alloc or new.
Some examples
Here I have used a name that does not imply transfer for ownership so I get a warning
- (id)object;
{
return [[NSObject alloc] init];
}
//=> Object leaked: allocated object is returned from a method whose name ('object') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa
Fix 1: (don't transfer ownership)
- (id)object;
{
return [[[NSObject alloc] init] autorelease];
}
Fix 2: (make the name more appropriate)
- (id)newObject;
{
return [[NSObject alloc] init];
}
With this knowledge we can of naming convention we can see that the below is wrong because we do not own the returned object
[self.object release]; //=> Produced warnings
And to show a final example - releasing an object that implies ownership transfer with it's name
[self.newObject release]; //=> No Warning

How do I fix this leak while using properties?

I am a noob with properties and how to manually release things since I typically use ARC but in this case I am not so I need a little help on how to fix the leak below. Now since it is a property, do I just stick a autorelease on the end or not. I am not sure! :)
Anyway if anyone can tell me how to properly get rid of this leak, that would be great!
- (id)init
{
self = [super init];
if (self)
{
self.purchasableObjects = [[NSMutableArray alloc] init];
self.storeObserver = [[ZTStoreObserver alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self.storeObserver];
}
return self;
}
This fixed both of the reported leaks:
- (id)init
{
self = [super init];
if (self)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
self.purchasableObjects = array;
[array release];
ZTStoreObserver *observer = [[ZTStoreObserver alloc] init];
self.storeObserver = observer;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self.storeObserver];
[observer release];
}
return self;
}
You shuold use strong, if you dont want to loose them.
#property(strong) NSMutableArray *purchasableObjects;
#property(strong) ZTStoreObserver *storeObserver;
I assume self.purchasableObjects must be strong reference(if it is weak, the memory will not be retained unless, there exist another strong reference to keep this object alive), Then you will not leak memory, but you may find total memory footprint increased, thats called allocation.To keep the memory footprint low, you can set that variable to nil and avoid retain cycles once you are done with that variable.You can probably forget about this variable until you receive a memory warning, at that point you can nil this object to reclaim the memory

NSMutableDictionary memory leak - how do I fix it without crashing the App?

I must have misunderstood some of the memory management rules, because when I try to fix a memory leak, the App crashes. Let me show you some code:
calendarRequestLog is a property of type MutableDictionary in a singleton object, that exists as long as the App runs. Here's the declaration in the .h file:
#property (nonatomic, retain, readonly) NSMutableDictionary *calendarRequestLog;
I allocate it with (in init):
calendarRequestLog = [[NSMutableDictionary alloc] init];
I fill it with this (notice the retain, that creates the memory leak):
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
I sometimes access it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
// add delegates
}
I empty it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
for (id <ServerCallDelegate> delegate in delegates) { … }
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
}
Here's the code that crashes when I remove the retain above:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
It crashes because delegates is deallocated but not nil. To be more precise, I get an EXC_BAD_ACCESS Exception.
All these methods may be called in different orders or multiple times.
I cannot figure out, why this happens. I thought, collections are supposed to retain their objects - as this array-object (delegates) is still in the collection, it should not be deallocated. Other code cannot be responsible, I showed you all occurrences of calendarRequestLog.
I appreciate all the help I can get!
#Edit
I think I got it.
I call the crashing method when the delegate gets deallocated, so that I do not call the delegate per accident later.
But: I retain the delegates in my calendarRequestLog, so it cannot get deallocated as long as this doesn't get called:
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
...which in turn, deallocates the delegate and calls the crashing method. As the calendarRequestLog has removed the delegates, but not yet the key, we crash.
Ok, I will solve this differently. Thanks for all the comments - thanks to you, I looked elsewhere!
Did you try retaining when fetching so nobody releases your object while you're using it?
NSMutableArray* delegates = [[calendarRequestLog objectForKey:date] retain];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
[delegates release];
Common practice is the following, because you already retain in the .h file:
//create local instance, then copy that to the class wide var
NSMutableDictionary *_calendarRequestLog = [NSMutableDictionary alloc] init];
self.calendarRequestLog = _calendarRequestLog;
[_calendarRequestLog release];
Also, I don't really understand why you would retain here:
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
Why not just change that to:
[calendarRequestLog setObject:[NSMutableArray arrayWithObject:delegate] forKey:date];
Write instead
calendarRequestLog = [[NSMutableDictionary alloc] init];
this
self.calendarRequestLog = [NSMutableDictionary dictionary];
and try to use property instead ivar

Resources