In our project, we have a series of classes that used to work fine.
In Xcode 4.1's latest update the behaviour seems to have changed, highlighting an issue in our code that wasn't detected before.
Here's the situation, using example classes to narrow the problem down:
Class Child inherits from class Parent
Class Parent declares (.h) an ElementalObject (but doesn't make it a property, because it's supposed to be protected, as in only used in Parent and its children) where #class ElementalObject is used.
Class Parent assigns the ElementalObject (imported with #import) in the .m
Class Child calls a method on the ElementalObject through [theElementalObject doStuff];
An error is shown at this step:
**ARC semantic issue:**
Receiver type 'ElementalObject' for instance message is a forward declaration
Since the Child class does not redeclare, or reassign, theElementalObject, I do not see why it should contain an import to the ElementalObject header file, which is already imported in the Parent implementation file.
It seems, unless I'm also misunderstanding that, that the compiler tells me otherwise.
Could anyone clear this up?
You do need to import ElementalObject.h in the child class's .m.
While the child class is not declaring the property, nor assigning it, it is trying to call methods on it. Unless the header for ElementalObject is imported, it has no idea what methods theElementalObject responds to.
Basically, if you do anything with a class, you must import the .h somewhere for the .m that uses it.
Related
I'm working on a framework.
The library is written in Swift and I notice that when a class inherits from NSObject or conforms to NSObjectProtocol, its declaration can be found in *.framework/Headers/*-Swift.h.
This class is available outside of the module in Objective-C code, so it became public.
Why does it happen if the access level is internal?
Internal Swift classes need to be available to the Objective-C code of that framework, however the only way Objective-C code can access the Swift classes is by importing the -Swift.h header file.
Now, how can a Swift class be visible to Objective-C: it either needs to inherit NSObject, or conform to NSObjectProtocol. If any of these two conditions is fulfilled, and the class declaration is not decorated with #nonobjc/private/fileprivate, then it will be exported to Objective-C via the -Swift.h module header.
This is why any Swift classes that are exportable to Objective-C will automatically be present in the discussed header file. It's an (unfortunate) coincidence that for frameworks this results in the class being publicly available (due to the fact that any Objective-C declarations that appear in a header are public).
Now, if you want your class to not end up in the -Swift.h header file, but still want to keep the NSObject(Protocol) inheritance/conformance, a workaround would be to make your class generic, thus prohibiting its exposure to Objective-C. Note that this will also prevent the class from being available to Objective-C code in the same framework.
// the generic argument doesn't matter, it's only used to make the class
// Swift-only
class MyClass<T>: NSObject { }
The caveat would be that every time the class is used, you will need to specify a value for the generic argument. This can be avoided by adding an intermediary base class:
// just a class that inherits NSObject, but is not exported in the -Swift header
class Empty<T>: NSObject { }
class MyClass: Empty<String> { }
I have a custom UIView class which needs to know about its parent (another different custom UIView class).
The parent class has to import the header of the child class, so it can add subviews of that class.
The child class has to import the header of the parent class, so it can access its methods and properties. It has to do the import in its .h file rather than its .m, because I need to make the child's parent an instance variable.
If I do this, I get circular import issues.
If anyone can make any sense of this, can you help to resolve this? Thanks.
What you want is commonly known as a forward declaration.
refer to Objective-C: Forward Class Declaration for more information
There are many ways to solve this, for example by just declaring the reference to the other class as an id, and to send forward messages to it (in Objective-C you don't even need to cast them, the compiler wouldn't complain about that).
For example:
#property(nonatomic,weak) id child;
But you may review your design in a way that you use a root controller that handles both the classes. This way A doesn't directly speak to B and B doesn't directly speak to A. Instead if A wants to speak with B, speaks with C and C speaks with B, and viceversa.
While you could use a forward declaration (#class ParentClass) and a weak reference to the parent (#property (nonatomic, weak) ParentClass *parent) in the child's header file, this is generally not a good programming practice.
Reasons why this is generally not a good idea:
1) As the project gets bigger, you're likely going to violate DRY ("don't repeat yourself") as the child necessitates a parent of a certain class... what if another parent later needs to create the same child object? You'd have to create a new class that declares another forward class of the new parent and has a weak property to it.
2) This is also likely going to lead to spaghetti code... what if you want to add a new feature to the parent that affects a method the child is using? Do you create a new yet similar method that's slightly different (see point 1 about violating DRY)? Do you create an input to the original method (you'd also have to make sure that the child now knows about this change and passes the appropriate input).
Instead, the Delegation design pattern works better here. Apple also frequently uses this throughout their libraries. In example, UITableView declares a delegate and a datasource so that it can delegate actions (clicks on cells) and data input (creation of custom cells) to other owning classes, without the UITableView object having to know about the implementation of said parent class.
For more information on the Delegation pattern in general, see Wikipedia on it here:
http://en.wikipedia.org/wiki/Delegation_pattern
For a tutorial on creating your own protocols (how delegation is implemented in iOS), see this tutorial here:
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-custom-delegates/
For high quality tutorials and introductions on iOS in general, including delegation and other necessary iOS concepts, see Ray Wenderlich's site also here:
http://www.raywenderlich.com/
Good luck!
I have a big class. To sum up, it has only one method that other classes should call and then a bunch of methods to do different stuff, depending on the type of data received. The .m file is getting so large that I want to split it up. My idea is to divide the methods among different categories (each category will work with a specific type of data). And so I did.
Inside of the main method I didn't change anything, so lines such as
[self methodNowInMyCategory];
are still there. However, I now get warnings in my class's main file since "instance method -methodNowInMyCatefory not found". The main .h file is importing the .h files of the categories, so the methods are visible (Xcode colors the code). The class just does not recognize them as its own.
Is there a way to, in a class, use a method declared in one of its categories? If not, which approach would you recommend me to solve my problem? The main method receives a file path, opens the file, analyzes the content and then sends it one or more methods.
You can split your category declarations into one or more .h files, and then import the category headers in your class's main .m.
I'm using a Objective-C framework for game development called Cocos2d-iphone.
This is how I create a button-graphic in the game:
CCMenuItemImage *battle;
battle = [CCMenuItemImage itemFromNormalImage:#"BattleFightOption1.png" selectedImage:#"BattleFightOption2.png"
target:self selector:#selector(battleFightOption)];
Basically, when the user clicks the button, method battleFightOption runs.
But I wonder, I never did define battleFightOption in the interface.. so, my question is: when is it necessary to define a method in the interface, and when is it not?
In short, every method that is meant to be used from outside the class must be declared in the interface; methods that are internal to the class implementation are omitted. The latter are typically declared in a class extension.
When you use a selector like #selector(methodName:), methodName: is called dynamically at runtime. The compiler doesn't have to know where it is, and doesn't check that the method exists when you compile.
However, it is still a good idea to declare it privately, which is generally done by putting an unnamed category at the top of the .m file (generally referred to as a class extension):
#import "Class.h"
#interface Class ()
- (void)privateMethod;
#end
#implementation Class
...
Anything that you intend to be public, called outside of the class, should be defined in the interface. If you are going to only use #selector(battleFightOption) you really do not need to define the method anywhere but I would recommend that you add a definition in the class extension just as you would any other private method.
I understand why I get the warning in the title when I define my own tableView property in my own class and then use a local variable name tableView.
What I want to know is why DON'T I get this warning when I derive my class from UITableViewController, which has it's own tableView property? Does the compiler/editor only look at my class and not the parent class?
When you’re implementing a method, parameters/local variables share the same namespace as instance variables. However, they don’t share the same namespace as declared properties, which means that a class can declare a property named someData (or inherit it from one of its superclasses), have the backing instance variable with some other name, and the implementation of a method of that class can also have a parameter/local variable named someData — the compiler won’t give a warning in that case.
I assume you have a declared property named tableView and also an instance variable named tableView, the latter being either explicitly declared in the interface or automatically created when synthesizing the property. In that case, if you define a method that takes a parameter named tableView or declares a local variable named tableView, this local declaration will hide the instance variable named tableView (but not the property).
In the case of UITableViewController, there is no instance variable named tableView. There is a declared property named tableView which, because it’s in a different namespace, won’t be hidden by a local (variable) declaration.
One easy fix to avoid the compiler warnings is to give a different name to the instance variable. For instance, the instance variable can be named _tableView, and the property would still be named tableView but synthesized as #synthesize tableView = _tableView.
Post the exact code that is generating the warning.
"Local declaration" typically implies that you have something like:
- (void) foo {
int thisIsTheNameOfAnInstanceVariable;
}
There are likely other permutations via which you could cause this to happen, though.
I'm not exactly sure if I'm answering this correctly, but if you want to access variables in the super classes (e.g. UITableView, since your class is deriving form it) you have to use "self." then the variable name form the super class. Whenever you directly call a variables, e.g. 'myVariable', it will only look for local instances.