Possible compiler bug in Xcode? - objective-c

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.

Related

Objective-C init quirks

In a class interface I define some ivar
#property (strong,nonatomic) id < Protocol > initEst; // Initial estimate
This compiles without a problem but when I run the program it crashes with EXC_BAD_ACCESS and [Object .cxx_destruct] indicated by the debugger as the reason.
What is going on?
It's all about the rules of ARC automatic memory management. An initializer has special rules for how it treats the returned value: it retains and returns. See https://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics-of-init.
Objective-C in general, and ARC in particular, have some pretty strict rules about what names of methods mean. initXXX means "this is an initializer". If this isn't an initializer, don't use the init prefix.
You could turn off ARC entirely and manage memory yourself, but it's easier just to obey the conventions, and it fits better in case of interaction with other languages (such as Swift).
I have tested this some more and there seems to be three conditions for this particular quirk to show up.
In my particular case the ivar's Protocol was also the same as that of the containing class. This seems to be an additional requirement for this problem to surface (refering here to my earlier answer that did not mention this).
So to elaborate on my earlier answer. If
initXXX is an ivar
of id type
that implements a Protocol that is the same as the containing class
then the Objective-C + ARC compiler will happily compile the code but be unable to execute it.
Here is a sample of the code I used to test
#interface Dog : NSObject < Animal >
#property (nonatomic,strong) id < Animal > initState;
#end
Something like this will cause problems simply because the name starts with init. Change the name and all problems disappear.
For reference, the runtime error this generates is
Dog object overreleased while already deallocating
This snippet is pretty abstract but this may bite you in places where you need to specify some initial condition and where it is natural to name some ivar initXxx but beware, if you use Objective-C you do not have that luxury nor will the compiler warn you that it is wrong.
The original error seemed memory allocation related and caused me to suspect the way I used the autoreleasepool but now I am fairly convinced this has nothing to do with the issue.

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

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.

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

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);

Persistent warning message about "initWithDelegate"!

This is not an actual Xcode error message, it is a warning that has been haunting me for
a long time. I have found no way of removing it and I think I maybe have overstepped some unwritten naming convention rule.
If I build a class, most often extending NSObject, whose only purpose is to do some task and report back when it has data, I often give it a convenience constructor like "initWithDelegate".
The first time I did this in my current project was for a class called ISWebservice which has a protocol like this:
#protocol ISWebserviceDelegate
#optional
- (void) serviceFailed:(NSError*) error;
- (void) serviceSuccess:(NSArray*) data;
#required
#end
Declared in my ISWebservice.h interface, right below my import statements.
I have other classes that uses a convenience constructor named "initWithDelegate".
E.g. "InternetConnectionLost.h", this class does not however have its methods as optional, there are no #optional #required tags in the declaration, i.e. they are all required.
Now my warning pops up every time I instantiate one of these Classes with convenience constructors written later than the ISWebservice, so when utilizing the "InternetConnectionLost" class, even though the entire Class owning the "InternetConnectionLost" object has nothing to do with the "ISWebservice" Class, no imports, methods being called, no nothing, the warning goes: 'ClassOwningInternetConnectionLost' does not implement the 'ISWebserviceDelegate' protocol
I does not break anything, crash at runtime or do me any harm, but it has begun to bug me as I near release. Also, because several classes use the "initWithDelegate" constructor naming, I have 18 of these warnings in my build results and I am getting uncertain if I did something wrong, being fairly new at this language.
Hope someone can shed a little light on this warning, thank you:)
The cause here is that you have multiple interfaces in your project that have functions defined as initWithDelegate:(id).
Alloc only returns id, so the compiler doesn't know the type, it therefore doesn't know which initWithDelegate is being used and seemingly uses the first one in the list. Thus you get a warning that makes no sense.
The runtime knows the type of the object however, and thus doesn't have any trouble.
The solution seemingly is to use more verbose init methods, eg. initWithWebServiceDelegate. Or to cast after the alloc:
[(Foo *)[Foo alloc] initWithDelegate:self]
Your getting the warning because the compiler thinks that the classes have declared that they implement the ISWebserviceDelegate protocol. I doubt it has anything to do with the intiWithDelegate method unless your inheriting the method or you've defined the method in a protocol that itself is an extension of ISWebserviceDelegate protocol.
The usual cause of this is declaring the adoption of the protocol in a superclass and then forgetting to implement in that superclass of a subsequent subclass.
If you're sure that is not the case, then you probably just need to clean the project and rebuild. You most likely did declare at one point, then undeclared but the compiler is stuck on the old declaration.
If the clean does not resolve, I would do a project find on ISWebserviceDelegate and make sure its not tucked away somewhere.