Objective-C: Instance variable used while 'self' not set... but it is - objective-c

I've been tasked with cleaning up some Clang errors in a code base. I am very new to iPhone development and Objective C, but have found most of the problems trivial... this one is stumping me though, when I'm sure its something embarrassing.
From a ZAttributedString class:
- (id)initWithAttributedString:(ZAttributedString *)attr {
NSParameterAssert(attr != nil);
if ((self = [super init])) {
_buffer = [attr->_buffer mutableCopy];
_attributes = [[NSMutableArray alloc] initWithArray:attr->_attributes copyItems:YES];
}
return self;
}
The clang warning is "Instance variable used while 'self' is not set to the result of '[super or self] init...]', with the dereferencing of attr's _buffer attribute being highlighted.
If it helps, the warning also seems to mention that the problem is found when calling from this method:
- (id)copyWithZone(NSZone *)zone {
return [(ZAttributedString *)[ZAttributedString allocWithZone:zone] initWithAttributedString:self];
}
Can anyone please explain to me what exactly the defect is here?
TIA!

Do not use -> to access instance variables, especially when the ivar is from some other object.
Do this:
_buffer = [[attr string] mutableCopy];
Same goes for that nasty attr->_attributes. Apparently, ZAttributedStringexposesattributes` as a property in the private header.
That compiler warning does seem, at the very most optimistic, entirely misleading and, likely, quite wrong in description. Filing a bug to have that clarified would be useful.
Note that #maddy's claim that using -> to access the instance variables directly in the attr string passed as it acts like a copy constructor is incorrect.
The incoming attr may be a ZAttributedString instance or an instance of a subclass or, really, an instance of any class that implements the same interface as ZAttributedString. Thus, you really must go through the accessors to guarantee that you are grabbing the correct state.
Now, as an implementation detail, ZAttributedString could require that the inbound instance be a non-subclassed instance of ZAttributedString, but it should use isMemberOfClass: to assert that requirement (and, please, don't do that).
The only spot where direct ivar access is sometimes used to pull state from another object is in the implementation of copyWithZone:, but that is exceedingly fragile and oft leads to whacky broken behavior. In fact, copyWithZone: (outside of the various plist compatible value classes) has been rife with fragility and the source of many many many bugs.

It seems like you are seeing the exact same bug as this: "[Bug 15092] New: static analyzer false positive: reports instance variable used while 'self' is not set to the result of [(super or self)] init". It has a very similar code attached to reproduce the bug.
If you run that code in Xcode 4.6.3 you can verify that it gives the same false warning as you are seeing.
The bug was resolved with the comment:
This is fixed in trunk, or at least mostly fixed -- there are still a few edge
cases where the warning will fire, but not your project.
(Dave, for now all of the main analyzer engineers do work at Apple, so there's
no real need to file duplicates. The LLVM people who don't work at Apple don't
have access to Apple Clang, which ships with Xcode, and this fix didn't make
Xcode 4.6. You can also get newer checker builds from
http://clang-analyzer.llvm.org)
As you can see the bug is fixed but still present in Xcode 4.6. Hold out for the next version of Xcode and the analyzer warning should be gone.

Related

In Objective C, why am I allowed to assign an NSArray to an NSMutableArray without error or warning?

I'm disturbed by a weird behavior, illustrated by the following example:
NSMutableArray *a1 = [[NSMutableArray alloc] init]; // fine
NSMutableArray *a2 = [NSMutableArray array]; // fine, too
// compiler reports incompatible pointer types; good:
NSMutableArray *a3 = [[NSArray alloc] init];
// compiler says nothing and is happy to assign this!
NSMutableArray *a4 = [NSArray array];
Both init and array method of both the NSArray and NSMutableArray classes return id. However, the behavior when I call these methods is simply not the same, and clang lets me happily assign an empty NSArray to an NSMutableArray variable!
It turns out that clang will automatically change the return type of some methods, including the init family, to instancetype, and thus be able to determine at compile time that [[NSArray alloc] init] returns an NSArray * and not an NSMutableArray *. But this check simply doesn't work with the array method.
Why? Shouldn't lines like my last example generate at least a warning? Why aren't all these methods declared as returning instancetype? Will it change in the future?
Update
Good news: as of iOS 7, [NSArray array] returns instancetype, so the assignment to a4 above also yields a warning. Other methods like arrayWithContentsOfFile: or arrayWithContentsOfURL still return id, though…
But this check simply doesn't work with the array method. Why?
As the document you have linked describes, it is because -array does not yield a recognized Related Result Type. ObjC is very dynamic -- the compiler cannot guarantee the result type of +array. It does make that assumption with some methods because the naming conventions are well defined (e.g. +alloc, -init, +new, -self, etc.). So this implementation simply resorts to naming conventions.
The compiler also validates some naming conventions in areas you may not expect:
#implementation NSArray (DEMO)
- (id)initStr
{
return [NSString new]; // << warning. RE: init prefix
}
#end
Shouldn't lines like my last example generate at least a warning? Why aren't all these methods declared as returning instancetype? Will it change in the future?
instancetype was introduced about one year ago (from the looks of it). Some of the APIs were written decades ago. I suspect it will happen -- in time -- because (if used correctly) it can point out a lot of issues in existing code. Of course, those changes would break existing builds (again, typically good corrections if declared in the right places).
So file bugs and give the tools and libraries a few years to update. Assuming the changes are made, it will probably happen at a major OS update.
It would probably be best if it were enabled as an optional warning for some time (in the case of the system headers). Of course, they could still employ it with backwards compatibility for older compilers for new APIs.
Also, this change could be retrofitted quite easily (not that earlier compilers would make sense of the semantic difference between id and instancetype) by a simple typedef. One problem with a typedef is that it is a global declaration -- a compiler could restrict a word/modifier/attribute to a given scope, without causing all the pain of simulating a keyword by adding a global typedef. Apple's GCC may never support instancetype, so the logical way to introduce it for Apple's GCC may be a global typedef of id, which could cause problems for some people (with no semantic benefit, if that route were taken). Note that similar breaking changes have been made by Apple in the past.
As it turns out, you're not just allowed to use the wrong array type, you're allowed to use the wrong type of any object with a convenience initializer that returns id. For example, this compiles without a warning in sight:
NSMutableArray *a4 = [NSDictionary dictionary];
This is a side effect of using id to opt out of type safety, and as you note, it should be deprecated behavior and replaced with instancetype (which does throw an incompatible type warning when used in the manner above).
Unfortunately, it's not a bug. instancetype being a fairly new keyword, it's adoption is not widespread yet, and it would be a bold move to start using it throughout Apple's frameworks. You never know, there's always hope for the next SDK!

Possible compiler bug in Xcode?

Here is the situation:
I have created a custom view class with UITextView as its member. I make textView as a first responder after some event occurs.
#inteface MyCustomView : UIView{
UITextView* textView;
}
- (void) doSomething;
#end
During program execution I send [doSomething] message to MyCustomView's instance.
[myCustomViewInstance doSomething];
What happens is very interesting, the program crashes by complaining that UITextView:doSomething is unrecognized selector.
Why is the program calling doSomething on UITextView (its member variable) instead of MyCustomView's instance.
What could be possibly wrong here ? (of course this is a canonical version of what I am doing but I don't understand in any logical error scenario this could be true unless something is messed up at compiler level)
What do you think I should investigate further ?
First, Objective-C doesn't have "member" variables, it has instance variables. Minor thing; but terminology does matter.
Secondly, the crash is happening because myCustomViewInstance is pointing to an instance of UITextView and not your subclass. This may be because you made such an assignment inadvertently or it may be because you have an over-release issue and it so happens that said variable ends up pointing to the wrong object by coincidence.
Finally, while there are certainly compiler bugs, it is exceedingly unlikely that such straightforward code would unveil a heretofore unknown compiler bug. The compiler you are using is the very same one that is used to compile the umpteen millions of lines of code of the OS; such a simple bug would very likely means no apps would work or, quite likely, the device wouldn't even boot.
That assumes that said line of code is actually what is crashing.

Consequences of a missing init?

In the process of porting a really badly coded iOS project to OS X, in which I make a point of preserving the model layer in order to (later) being able to keep the two versions in sync.
I do not currently have access to change the iOS code base - and don't particularly want to, either. Also, for all its faults, the model layer is tested and working.
If it ain't broke don't fix it, they say. So I guess my question is, is the code below broke or not? Notice there is no call to init after the alloc, and the class being instantiated is a direct subclass of NSObject.
...
SuspectClass *obj = [SuspectClass alloc];
obj.arrayProperty = [NSArray arrayWith...];
// etc.
...
I guess another way to put the question is if NSObject's init actually adds anything to an object?
From the documentation of init in NSObject comes the official answer: "An object isn’t ready to be used until it has been initialized."
...and the practical answer: "The init method defined in the NSObject class does no initialization; it simply returns self."
:-)
Though functional, I don't think I'd trust a bare alloc given the number of places that warn that some form of init is required.
without init you get an "object" (at least enough space in memory for the 'object') in an undefined state. You cannot know what happens to that place in your memory, so init is a must

Private Methods in Objective-C, in Xcode 4.3 I no longer need to declare them in my implementation file ?

I have a lot question marks tolling above my head.
What I don't get is before xcode 4.3 I needed to declare forward declarations (for private methods) in my implementation file.
Like in my .m file:
// deleting this with xcode 4.3 the below code still does work
// in previous versions i had to put this because otherwise the compiler can't find methodFirst
#interface DetailViewController ()
- (void)methodFirst;
- (void)methodSecond;
#end
#implementation DetailViewController
- (void) methodSecond
{
// if i delete the forward declaration now adays i dont get a compiler error that he cant find method first
[self methodFirst];
}
- (void) methodFirst
{
}
#end
Now it seems I don't need to do that anymore? Did Apple update the compiler so that it isn't needed anymore to put forward declarations?
I can't find any reference to an official Apple source about this change. I wonder what other people have encountered in their new environment.
As of the LLVM Compiler version shipped with Xcode 4.3, if you try to call a method that the compiler has not previously seen, it will look in the rest of the current #implementation block to see if that method has been declared later. If so, then it uses that, and you don't get a warning. Hence, as of Xcode 4.3, there's much less need to pre-declare your internal methods. Clearly, you still need to declare methods that are publicly exposed to other classes.
This change was noted in the release notes of some of the Xcode 4.3 betas, but apparently didn't make it into the "What's New in Xcode 4.3" final documentation.
Unlike has been suggested in other answers, this is not just an "Undeclared Selector" warning that has been turned off by default. In fact, if you're using ARC, unrecognized selectors are still hard errors. Try calling [self myNonexistentMethod] and you'll see; the compiler still complains.
There aren't any private methods in Objective-C. What you're thinking of is the class continuation, the "nameless" category interface you can declare in your .m file to declare methods that will be in the class implementation, but that aren't in the public interface.
There's never been a requirement to declare methods before they're used. However, it's always been a good idea, and the compiler has a warning flag to indicate when methods that haven't been seen are used. The reason is to do with the operating system's calling convention for functions. Different types, such as structures, floating point numbers, integer numbers, and pointers, can all be handled in different ways when they are the arguments to or return values from functions. Indeed, on different computers and in different operating systems, they are handled in different ways. To know how to handle the arguments and return values for an Objective-C method, the compiler needs to know the signature for that method: how many arguments of what types it takes, and what type it returns.
If it hasn't seen a declaration of the method, then the compiler will need to make a guess. If that guess is incorrect, then it can end up putting the wrong values into the arguments, or interpreting the return value incorrectly, or trying to take something off the stack that doesn't exist.

Did the Target-Action design pattern became bad practice under ARC?

For years I've been following a great pattern called Target-Action which goes like this:
An object calls a specified selector on a specified target object when the time comes to call. This is very useful in lots of different cases where you need a simple callback to an arbitrary method.
Here's an example:
- (void)itemLoaded {
[specifiedReceiver performSelector:specifiedSelector];
}
Under ARC it now turns out that doing something like this all of a sudden became dangerous.
Xcode throws a warning that goes like this:
PerformSelector may cause a leak because its selector is unknown
Of course the selector is unknown since as part of the Target-Action design pattern you can specify whatever selector you want in order to get a call when something interesting happens.
What bugs me most about this warning is that it says there can be a potential memory leak. From my understanding ARC doesn't bend the memory management rules but instead simply automates the insertion of retain/release/autorelease messages at the right locations.
Another thing to note here: -performSelector: does have an id return value. ARC analyzes method signatures to figure out through application of naming conventions if the method returns a +1 retain count object or not. In this case ARC doesn't know if the selector is a -newFooBar factory or simply calling an unsuspicious worker method (which is almost always the case with Target-Action anyways). Actually ARC should have recognized that I don't expect a return value, and therefore forget about any potential +1 retain counted return value. Looking at it from that point of view I can see where ARC is coming from, but still there is too much uncertainty about what this really means in practice.
Does that now mean under ARC something can go wrong which would never happen without ARC? I don't see how this could produce a memory leak. Can someone give examples of situations in which this is dangerous to do, and how exactly a leak is created in that case?
I really googled the hell out of the internet but didn't find any site explaining why.
The problem with performSelector is that ARC doesn't know what the selector which will performed, does. Consider the following:
id anotherObject1 = [someObject performSelector:#selector(copy)];
id anotherObject2 = [someObject performSelector:#selector(giveMeAnotherNonRetainedObject)];
Now, how can ARC know that the first returns an object with a retain count of 1 but the second returns an object which is autoreleased? (I'm just defining a method called giveMeAnotherNonRetainedObject here which returns something autoreleased). If it didn't add in any releases then anotherObject1 would leak here.
Obviously in my example the selectors to be performed are actually known, but imagine that they were chosen at run time. ARC really could not do its job of putting in the right number of retains or releases here because it simply doesn't know what the selector is going to do. You're right that ARC is not bending any rules and it's just adding in the correct memory management calls for you, but that's precisely the thing it can't do here.
You're right that the fact you're ignoring the return value means that it's going to be OK, but in general ARC is just being picky and warning. But I guess that's why it's a warning and not an error.
Edit:
If you're really sure your code is ok, you could just hide the warning like so:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[specifiedReceiver performSelector:specifiedSelector];
#pragma clang diagnostic pop
The warning should read like this:
PerformSelector may cause a leak because its selector is unknown. ARC doesn't know if the returned id has a +1 retain count or not, and therefore can't properly manage the memory of the returned object.
Unfortunately, it's just the first sentence.
Now the solution:
If you receive a return value from a -performSelector method, you can't do anything about the warning in code, except ignoring it.
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
Your best bet is this:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
#pragma clang diagnostic pop
Same goes for the case in my initial question, where I completely ignore the return value. ARC should be intelligent enough to see that I don't care about the returned id, and therefore the anonymous selector is almost guaranteed not to be a factory, convenience constructor or whatsoever. Unfortunately ARC is not, so the same rule applies. Ignore the warning.
It can also be done for the whole project by setting the -Wno-arc-performSelector-leaks compiler flag under "Other Warning Flags" in project build settings.
Alternatively, you can surpress the warning on a per-file basis when you add that flag under Your Target > "Build Phases" > "Compile Sources" on the right-hand side next to the desired file.
All three solutions are very messy IMHO so I hope someone comes up with a better one.
As described above you get that warning because the compiler does not know where (or if) to put the retain/release of the performSelector: return value.
But note that if you use [someObject performSelector:#selector(selectorName)] it will not generate warnings (at least in Xcode 4.5 with llvm 4.1) because the exact selector is easy to be determined (you set it explicitly) and that's why compiler is able to put the retain/releases in the correct place.
That's why you will get warning only if you pass the selector using SEL pointer because in that case the compiler is unable to determine in all case what to do. So using the following
SEL s = nil;
if(condition1) SEL = #selector(sel1)
else SEL = #selector(sel2)
[self performSelector:s];
will generate warning. But refactoring it to be:
if(condition1) [self performSelector:#selector(sel1)]
else [self performSelector:#selector(sel2)]
will not generate any warnings
ARC is throwing the warning because it can't guarantee that the selector isn't creating an object it doesn't know about. You could theoretically receive something from that method that ARC can't handle:
id objectA = [someObject performSelector:#selector(createObjectA)];
Maybe someday it can, but right now it can't. (Note if it does know the object (it's not an id) it doesn't throw this warning).
If you're trying to simply execute a method without receiving an object back from it, I recommend using objc_msgSend. But you've gotta include in your class:
#include <objc/message.h>
objc_msgSend(someObject, action);