What are the rules that affect __weak variables in a block with regard to GCC_OPTIMIZATION_LEVEL? - objective-c

In the code below, when I run it with the GCC_OPTIMIZATION_LEVEL set to None, execution works as I expect and it prints the following to the console:
My Obj - SomeObj
However, when I set the GCC_OPTIMIZATION_LEVEL to Fastest, Smallest in the Xcode target configuration (as one typically does for Release builds) I end up with the following printed to the console:
My obj - (null)
The object appears to be getting released when I store it into the __weak id myObj variable in [Foo doSomething]. If I remove the __weak flag from the myObj variable the code runs as expected when the GCC_OPTIMIZATION_LEVEL is set to Fastest, Smallest.
I built this example based on a similar pattern I had in another project and added the __weak flag because I was encountering a retain cycle. The retain cycle warning went away, but when I built for Release, I found that myObj would be nil by the time it got to the spot where I am logging it in this example.
What rules am I violating by setting the __weak flag?
#import "FFAppDelegate.h"
///////////////////////////////////////////////////////
#interface SomeObject : NSObject
#end
#implementation SomeObject
- (NSString *)description; {
return #"SomeObject";
}
#end
///////////////////////////////////////////////////////
#interface Factory : NSObject
#end
#implementation Factory
- (id)generateObj {
id myObj = nil;
if (!myObj) {
SomeObject *anObj = [[SomeObject alloc] init];
myObj = anObj;
}
return myObj;
}
#end
///////////////////////////////////////////////////////
#interface Bar : NSObject
- (id)barObj;
#end
#implementation Bar
{
Factory *factory;
}
- (id)init {
self = [super init];
if (self) {
factory = [[Factory alloc] init];
}
return self;
}
- (id)barObj {
id anObj = [factory generateObj];
return anObj;
}
#end
///////////////////////////////////////////////////////
#interface Foo : NSObject
#property (strong) Bar *aBar;
- (void)doSomething;
#end
#implementation Foo
- (id)init {
self = [super init];
if (self) {
_aBar = [[Bar alloc] init];
}
return self;
}
- (void)doSomething {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
__weak id myObj = [self.aBar barObj];
NSLog(#"My Obj - %#", myObj);
});
}
#end
///////////////////////////////////////////////////////
#implementation FFAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Foo *f = [[Foo alloc] init];
[f doSomething];
}
#end

What rules am I violating by setting the __weak flag?
In your example, there is no strong reference to the instance returned from [self.aBar barObj]. Therefore, you cannot assume the instance will be alive by the time you read the myObj variable.
In the unoptimized build, the returned instance has been added to an autorelease pool and is therefore still alive when you print myObj. You can verify this by setting a breakpoint in -[SomeObject dealloc].
In the optimized build, ARC has avoided the autorelease pool and the returned instance is therefore deallocated immediately.
See section 3.2.3 in the Clang ARC documentation for more info. Specifically:
When returning from such a function or method, ARC retains the value at the point of evaluation of the return statement, then leaves all local scopes, and then balances out the retain while ensuring that the value lives across the call boundary. In the worst case, this may involve an autorelease, but callers must not assume that the value is actually in the autorelease pool.
When using __weak to avoid a retain cycle, you must ensure that there is a strong reference somewhere else to keep the object alive.

Maybe you should use Apple's default compiler LLVM and not GCC.
Edit: Also your "factory" method looks wrong. For example, the object is always nil in that if. Take a tour: http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/ClassFactoryMethods/ClassFactoryMethods.html

Related

Calling getter for dispatch_queue_t property causes crash

I have a private serial queue declared as a property and I am running into a very strange situation.
If I dispatch_async the property, it will crash (EXC_BAD_ACCESS (code=EXC_i386_GPFLT)). After some debugging, I found out that it's because the getter is called. If getter is not called, the crash won't happen. Also, it always crashes the second time self.queue is called. See second example below.
It's as though the first synthesized getter call has somehow caused the ivar to be over-released.
This is targeting iOS 9 and above so I am not checking OS_OBJECT_USE_OBJC.
Example 1) This doesn't work:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
// Crashes here - EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
// the second time self.queue is accessed - either by subsequent call into
// this method, or by adding NSLog(#"%#", self.queue) before this line.
dispatch_async(self.initQueue, ^{
...
});
}
Example 2) This also doesn't work:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
NSLog(#"%#", self.initQueue);
// Crashes below - EXC_BAD_INSTRUCTION (code=EXC_i386_INVOP, subcode=0x0)
NSLog(#"%#", self.initQueue);
}
Example 3) It works if I stay away from using the getter:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
// Works fine
dispatch_async(_initQueue, ^{
...
});
}
Example 4) It also works if I supply the getter:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (dispatch_queue_t)initQueue {
return _initQueue;
}
- (void)onCompletion:(void (^)())completion {
// Works fine
dispatch_async(self.initQueue, ^{
...
});
}
Example 5) It will also work if I use ivar for queue instead of property or self.initQueue is assigned the main queue instead.
What's the reason for this behavior?
Other open source library is using property for dispatch_queue_t along with the getter and they have no issue at all. Example: https://github.com/rs/SDWebImage/blob/7e0964f8d90dcd80d535c52dd9f6d5fa7432052b/SDWebImage/SDImageCache.m#L57
Per your comments you originally named the property initQueue, this in turn created a method called initQueue which fell afoul of the ARC Method family rules. Those rules indicate that ARC will automatically annotate any method beginning with new or init as NS_RETURNS_RETAINED.
Methods in the init family implicitly consume their self parameter and return a retained object. Neither of these properties can be altered through attributes.
This in turn means that callers of the method are supposed to be safe to assume that they are taking ownership of the returned value and do not need to increment the retain value. As a result when you attempted to use the property ARC did not increase the reference count as was expected but ARC still left a release call at the end of the method. This resulted in your property value being released prior to when your class was dealloced.
It is possible to override this behavior using attributes in some cases. However I would suggest just being aware of method families, as they can have a nice performance impact on your application particularly for factory methods.
Other pitfalls to be aware of:
Methods in the alloc, copy, mutableCopy, and new families — that is, methods in all the currently-defined families except init — implicitly return a retained object as if they were annotated with the ns_returns_retained attribute. This can be overridden by annotating the method with either of the ns_returns_autoreleased or ns_returns_not_retained attributes.
A side note on this as well:
It is undefined behavior for a program to cause two or more calls to init methods on the same object, except that each init method invocation may perform at most one delegate init call.
Sadly the compiler doesn't seem to warn about that one.

Objective-C, ARC: Is it correct to use __weak arguments?

Here's a little example of using weak arguments:
#interface MYTestObject : NSObject
#end
#implementation MYTestObject {
void(^_block)(void);
}
- (void)dealloc {
NSLog(#"DEALLOC!");
}
- (id)init {
if (self = [super init]) {
[self doSomethingWithObject:self];
}
return self;
}
- (void)doSomethingWithObject:(id __weak /* <- weak argument! */)obj {
_block = ^{
NSLog(#"%p", obj);
};
}
#end
And it works: -dealloc is called!
Also, if you remove __weak you'll get a retain-cycle and it's absolutely correct.
Wonder, if that's just a side-effect and it's completely unsafe to use weak arguments? Or is it a specified behavior and I'm just a bad google-user?
Two observations:
I'd be inclined to make a local __weak reference within doSomethingWithObject, rather than make it a __weak argument as illustrated in Avoid Strong Reference Cycles when Capturing self.
I don't think that it is, as you asked, "completely unsafe to use weak arguments." But if nothing else, it's the more common pattern to have a local __weak variable and strikes me as more appropriate as an implementation detail of doSomethingWithObject rather than part of the method's public interface.
I'd also make block a property with the copy memory qualifier. As the docs say
You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it's best practice for the property attribute to show the resultant behavior.
Thus:
#interface MYTestObject : NSObject
#property (nonatomic, copy) void(^block)(void);
#end
#implementation MYTestObject
- (void)dealloc {
NSLog(#"DEALLOC!");
}
- (id)init {
if (self = [super init]) {
[self doSomethingWithObject:self];
}
return self;
}
- (void)doSomethingWithObject:(MYTestObject *)obj {
typeof(obj) __weak weakObj = obj;
self.block = ^{
NSLog(#"%p", weakObj);
};
}
#end

a puzzle on Objective-C memory management

This is one segment of codes used in one of my project for managing one of my class instance:
#interface C: NSObject
{
NSMutableArray *myArr;
}
#property (nonatomic,retain) NSMutableArray *myArr;
//...
#end
#implementation C
#synthesize myArr;
//...
-(id)init
{
//...
myArr = [[NSMutableArray alloc] init];
//...
}
//version 1 of dealloc method
-(void)dealloc
{
//...
self.myArr = nil;
//...
}
//version 2 of dealloc method
-(void)dealloc
{
//...
[myArr release];
//...
}
here the version 1 dealloc method doesn't work and Xcode says something like "EXC_BAD_ACCESS..." and the app crashed.
if I modified the dealloc method as version 2, it works.
Does anybody have any idea why?
Thx in advance.
As Duncan said, the EXEC_BAD_ACCESS error means that the object doesn't exist anymore.
This is probably due to the fact that myArr is being released before the dealloc gets called.
To facilitate memory management and to keep track of reference counts, I like to make it clearer in the init methods, for example:
-(id)init
{
//...
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0];
self.myArr = array;
[array release];
//...
}
By using the generated setter self.myArr = array; you are ensuring that the reference count is being delt with correctly, it will release the old value and retain the new one for you.
#MadhavanRP : if the property is a retained property:
#property(nonatomic, retain) NSMutableArray *myArr;
calling
self.myArr = nil
is exactely the same as calling
[myArr release];
myArr = nil;
Edit: #Sylvain beat me to it :)
This is OK even if it's bad idea/confusing to have same name for iVar and property. I removed the iVar declaration.
#interface C: NSObject
{}
#property (nonatomic,retain) NSMutableArray *myArr;
//...
#end
Generate your iVar using #synthetize.
#implementation C
#synthesize myArr = _myArr;
//...
Your init is all wrong. You are assigning the iVar without using the setter method.
-(id)init
{
//...
// You were doing
// _myArr = [[NSMutableArray alloc] init];
// You should do
NSMutableArray array = [[NSMutableArray alloc] init];
self.myArr = array;
[array release];
// You could avoid the temporary variable but this confuse XCode static analyzer
//...
}
This is OK. I guess that #synthetize generated another iVar to back your property.
This other iVar was not properly assign.
You would not notice this if you do not user self.myArr (or the equivalent [self myArr]).
This confusion is main reason why hardcore ObjC programmer do not like the new property thing. :) This confuse new programmers.
//version 1 of dealloc method
-(void)dealloc
{
//...
self.myArr = nil;
//...
}
This is not good as you bypass the setter, as in init method. This was working anyway.
//version 2 of dealloc method
-(void)dealloc
{
//...
[myArr release];
//...
}
It looks like your myArr object is being messaged when it should not be, in the first case you hide the problem by setting it to nil, in the second you don't. Either method should not crash. When do you call [super dealloc]; (you should call it at the end of the dealloc implementation).

Initializing objects using Objective-C, clarification needed

if header file declares
#interface SomeClass: NSObject {
Data* d;
}
#property (nonatomic, retain) Data* d;
Why is the following line in the implementation file giving me a warning (and init method does not get called?)
[[[self d] alloc] init];
The warning i get is
Instance method '-alloc' not found (return type defaults to 'id')
Meanwhile, Data has
- (id) init method, that is not being called.
Please help me understand why.
alloc should be invoked on a class, not on an instance.
interface SomeClass : NSObject
{
Data *d;
}
Declare an init method on SomeClass and make it look like:
- (id) init
{
self = [super init];
if (self)
{
d = [[Data alloc] init];
}
return self;
}
- (void) dealloc
{
[d release];
[super dealloc];
}
Now you do:
SomeClass *c = [[SomeClass init] alloc];
And you can use the class. Note that you should probably read a little more on classes and objects and about memory management too (when you should release c, etc.).
If, by any chance, you have the possibility to use ARC (automatic reference counting), you won't need to take care of releasing stuff. But that doesn't come with Xcode 4.1, only with 4.2 which is not publicly accessible, apparently.
The problem isn't -(id)init, it's -(id)alloc. alloc is a class method of NSObject, which means you send it to the class itself and not to an instance of that class, i.e.:
[Data alloc]; // Correct
[someDataInstance alloc]; // Method not found
When you call [self d], you're given an instance of a Data, which you're then sending a -(id)alloc message to. Since NSObject doesn't have a -(id)alloc (only a +(id)alloc), you get the warning.
You should be doing
self.d = [[Data alloc] init];
As Matt says, alloc is a class method, and must be called on the class itself.

Problem with releasing an object

I've got this code:
Entry.h
#import <Foundation/Foundation.h>
#interface Entry : NSObject {
id object;
SEL function;
}
#property (retain) id object;
#property (assign) SEL function;
-(Entry*) initWithObject:(id)object selector:(SEL)function;
#end
Entry.m
#import "Entry.h"
#implementation Entry
#synthesize object;
#synthesize function;
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
[self setObject:obj];
[self setFunction:sel];
return self;
}
-(void) dealloc {
[super dealloc];
if ([self object] != nil)
[[self object] release];
}
#end
And when I do this:
Entry *hej = [Entry alloc];
[hej release];
I get:
objc[2504]: FREED(id): message object sent to freed object=0xf5ecd0
Program received signal: “EXC_BAD_INSTRUCTION”.
What am I doing wrong?
(And this insert code thing at stack overflow doesnt work, unless I'm doing something wrong and you're not supposed to click "code sample" and then paste.)
+alloc only allocates memory. You need -init to actually create the object in that memory space. Since you are only allocating memory and not creating an object there, calling -release on a chunk of memory is giving you an error. Further, you want your [super dealloc] call to appear at the end of you -dealloc method. Change those two things and the following should work:
Entry *hej = [[Entry alloc] init];
[hej release];
there are two problems here:
1) you need to check that self = [super init] does not return nil. Typical usage would be to follow wrap your initialization code with the conditional:
if ((self = [super init]) != nil) {
// initialize the object...
}
2) but where you are getting stuck is on instantiating your object: you should do it like this:
Entry *hej = [[Entry alloc] initWithObject:myObj selector:mySelector];
(assuming that you want to go through the custom initializer you just defined...
else just use the default init method.) but 'alloc' must be followed by an init.
Entry *hej = [[Entry alloc] init]; // will do the trick...
Firstly, you need an init to go with your alloc. Second, in dealloc, you send a message to self after calling [super dealloc]. You can't do that. The final deallocation should go at the end.
I would also recommend changing:
if ([self object] != nil)
[[self object] release];
to:
[self setObject:nil];
It's less code and functionally equivalent. =)
There are many things wrong with your code. I'll try to go through them.
First, its better to use a different ivar name to your property name so its clear where you are using each. Apple normally uses an underscore prefix, but any prefix will do.
#interface Entry : NSObject {
id _object;
SEL _function;
}
#property (retain) id object;
#property (assign) SEL function;
#synthesize object = _object;
#synthesize function = _function;
Next, you aren't using the standard init template (although this probably wont make any difference normally).
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
// initializations
}
return self;
}
Next, Apple (for good reasons) recommends against using getters/setters in your init/dealloc. So your init would be:
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
_object = [obj retain];
_object = sel;
}
return self;
}
Next, after [super dealloc] your object is destroyed, so you cannot reference self (and hence your ivars) after that, so your dealloc should look like:
-(void) dealloc {
// your deallocations
[super dealloc];
}
Further, as above, Apple recommends you should not use setters or getters in your dealloc routine, so your deallocation would initially look like:
if (_object != nil)
[_object release];
But further still, Objective C allows (and Cocoa encourages) that sending a method to nil does nothing. This is in stark contast to most other languages where messaging nil would cause a crash, but it is how Objective C/Cocoa work and you need to get used to it. So your deallocation is actually just:
[_object release];
And finally, alloc only allocates the memory for your object, you have to initialize it, so the initialization would be something like:
Entry *hej = [[Entry alloc] initWithObject:myobj selector:#selector(mymethod)];