ARC Semantic Issue with simple protocol example? - iphone

I have just been trying something out with a quick test and I have a question, in the following code:
#protocol stuffieProtocol <NSObject>
#required
-(void)favouiteBiscuit;
#end
.
// DOG & TED ARE IDENTICAL, THEY JUST LIKE DIFFERENT BISCUITS
#interface Dog : NSObject <stuffieProtocol>
#property (strong, nonatomic) NSString *name;
#end
#implementation Dog
- (id)init {
return [self initWithName:#"Unknown"];
}
- (id)initWithName:(NSString *)name {
self = [super init];
if(self) {
_name = name;
}
return self;
}
- (void)whoAreYou {
NSLog(#"MY NAME IS: %# I AM A: %#", [self name], [self class]);
}
- (void)favouiteBiscuit {
NSLog(#"FAVOURITE BISCUIT IS: Costa Jam Biscuit");
}
#end
.
Dog *stuffie_001 = [[Dog alloc] initWithName:#"Dog Armstrong"];
Ted *stuffie_002 = [[Ted alloc] initWithName:#"Teddy Sullivan"];
NSArray *stuffieArray = #[stuffie_001, stuffie_002];
for(id<stuffieProtocol> eachObject in stuffieArray) {
[eachObject whoAreYou]; // << ERROR
[eachObject favouiteBiscuit];
}
My question is I am getting an error "ARC Semantic Issue: No known instance method for selector 'whoAreYou'"
If I prefix [eachObject whoAreYou]; with [(Dog *)eachObject whoAreYou]; then this works for all the iterations of the loop, but that just feels wrong as the all the objects in the array are not of type Dog.
What should I be prefixing this with to be correct?

Add
-(void) whoAreYou;
to your protocol. Then the compiler knows that eachObject in the loop responds to that method.

well, you declare eachObject as an ID
that's mean that the compiler doesn't know what kind of object it is
it just know that it implements the protocol stuffieProtocol, so surely it can respond to method: favouiteBiscuit
but it doesn't know if it can respond to method whoAreYou
you can do many thing to avoid this
the easiest is:
you could ask if eachObject can perform the selector whoAreYou, and in this case you perform that selector
if ([eachObject respondsToSelector:#selector(whoAreYou) ]) {
[eachObject performSelector:#selector(whoAreYou) ];
}
this way the compiler avoid to control if eachObject implement the method whoAreYou
it will be done at runtime, so if there a method whoAreYou in eachObject, then ok, it will be called
Another way could be to make a common superclass for both ted and dog
(e.g.
SuperClassOfTedAndDog <stuffieProtocol>
)
and declare method whoAreYou in that superclass, then in your for loop use that superclass instead of ID:
for(SuperClassOfTedAndDog* eachObject in stuffieArray) {
[eachObject whoAreYou];
[eachObject favouiteBiscuit];
}

Related

To define and use convenient constructors without defining init methods is bad practice?

Normally, you define init or initWith... methods and call them inside convenient constructors like this:
#implementation MyClass1 : NSObject
#synthesize n, s;
-(id)init
{
self = [super init];
if (self) {
self.n = 1;
}
return self;
}
-(id)initWithString:(NSString *)s
{
self = [self init];
if (self) {
self.s = s;
}
return self;
}
+(id)myClass
{
return [[self alloc] init];
}
+(id)myClassWithString:(NSString *)s
{
return [[self alloc] initWithString:s];
}
#end
But I think it is possible to define convenient constructors without defining init or initWith... methods like this:
#implementation MyClass2
#synthesize n, s;
+(id)myClass
{
MyClass2 *obj = [[self alloc] init];
obj.n = 1;
return obj;
}
+(id)myClassWithString:(NSString *)s
{
MyClass2 *obj = [self myClass];
obj.s = s;
return obj;
}
#end
Is it bad practice to define convenient constructors without defining init method?
If it is bad practice, could you tell me the disadvantage or problems?
I'm not sure if it's actually a bad practice. Generally, when I write convenience constructors they look like this:
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
{
FSClub *club = [[FSClub alloc] init];
if (club)
{
club.identifier = [element integerValueWithPath:#"id"];
club.name = [element valueWithPath:#"naam"];
club.referer = [element URLWithPath:#"referer"];
}
return club;
}
The code still takes into account possible memory issues (initialisation failure) like in a 'normal' init constructor. Values will only be set if initialisation is successful.
The interface file is defined as such:
#interface FSClub : NSObject
#property (nonatomic, assign, readonly) NSInteger identifier;
#property (nonatomic, copy, readonly) NSURL *referer;
#property (nonatomic, copy, readonly) NSString *name;
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
#end
Please note the properties are readonly. I prefer creating immutable objects, since they're easier to deal with in e.g. threaded code. The only way to have the properties set in this situation is by using the convenience constructor.
When I create the convenience constructors, these are generally the only methods I use to instantiate the objects. That is to say, the -init method will not be used most of the time. Also, writing lots of initialisers even if you don't use them takes lots of developer time. I wouldn't create methods that I don't use.
When you create code as part of a framework -code that you share with lots of people that you don't know- in such situations you might want to write both the convenience constructors as well as all the normal constructors, because you can't be sure how the code will be used in people's own project. For example creating lots of objects using convenience constructors in tight loops might be bad for performance, since the objects are added to the autorelease pool. I think this is also true in an ARC scenario. In such situations one might have the option to use 'normal' constructors to create objects.
//I think it is BAD. First of all, you confused CLASS and OBJECT, you get an object like this
// [[CLASSNAME alloc] init];
//not like this:
// [[OBJECT alloc] init];
//so, what you want to do shall be like this:
// +(id)myClass
// {
// MyClass2 *obj = [[[self class] alloc] init];
// obj.n = 1;
// return obj;
// }
PLEASE ignore above all, thanks to #W'rkncacnter.
however, here you are returning an object owned by yourself without autorelease, it's not recommended.
Maybe what you really want is something like factory method?
+(id)myObjectWithString:(NSString *)string
{
MyClass2 *obj = [[[MyClass2 alloc] init] autorelease];
obj.s = string;
return obj;
}
Doing it like you did with MyClass1 makes it easier to define a designated initializer. Apple recommends these; they help reducing code repetition.

Instance class or method without an interface?

Got this code:
#import <Foundation/Foundation.h>
#interface CalculatorBrain : NSObject
- (void)pushOperand:(double)operand;
- (double)performOperation:(NSString *)op;
#property (nonatomic, readonly) id program;
+ (NSString *)descriptionOfProgram:(id)program;
+ (double)runProgram:(id)program;
#end
And this one:
#import "CalculatorBrain.h"
#interface CalculatorBrain()
#property (nonatomic, strong) NSMutableArray *programStack;
#end
#implementation CalculatorBrain
#synthesize programStack = _programStack;
- (NSMutableArray *)programStack
{
if (_programStack == nil) _programStack = [[NSMutableArray alloc] init];
return _programStack;
}
- (id)program
{
return [self.programStack copy];
}
+ (NSString *)descriptionOfProgram:(id)program
{
return #"blablabla";
}
- (void)pushOperand:(double)operand
{
[self.programStack addObject:[NSNumber numberWithDouble:operand]];
}
- (double)performOperation:(NSString *)operation
{
[self.programStack addObject:operation];
return [[self class] runProgram:self.program];
}
+ (double)popOperandOffProgramStack:(NSMutableArray *)stack
{
double result = 0;
return result;
}
+ (double)runProgram:(id)program
{
NSMutableArray *stack;
if ([program isKindOfClass:[NSArray class]]) {
stack = [program mutableCopy];
}
return [self popOperandOffProgramStack:stack];
}
#end
The code is fine an it runs, so the question is, Where is declared popOperandOffProgramStack in the interface? why it compiles and it's okay? it should crash but I can not find an explanation to this....
Thank you!
You only need to declare methods in the #interface in the .h file if you're exposing them to the world. Otherwise, no declaration needed.
And nowadays, the order that they appear in the implementation doesn't matter, either. Historically, if the method was implemented later in the #implementation than where it was invoked, you needed to have the method declared above (generally in the #interface). Now the compiler doesn't care whether the implementation is earlier or later in the .m file.
the compiler can sees its definition:
+ (double)popOperandOffProgramStack:(NSMutableArray *)stack
{
double result = 0;
return result;
}
so it is able to confirm it has been declared, the parameter types, and return type.
also - in older compilers, it would need to precede usage, but not anymore if used in the #implementation scope.
even if it were not declared, objc is weak enough that it would not be a compiler error (warning, perhaps). exception: the method must be visible if you're using ARC. the compiler needs to know the reference counting semantics and parameter types when ARC is enabled.

Passing a pointer by reference to class and using it

I am trying to pass a pointer by reference to an object from class A to class B. In class B I want to assign this pointer to a ivar and read and write to it.
This is the code that gives me errors (does not matter what errors). This is my first try with pointers so please correct my understanding.
Class A
//This is the parameter I would like to pass as a pointer and be able to manipulate from class B
NSString *name = #"Cyprian";
-(void)passAParameter{
ClassB *classB = [[ClassB alloc] initWithAPointer:&name];
...
}
Class B
// ClassB.h
#interface ClassB{
NSString **nameFromClassA;
}
#property(nonatomic,assign)NSString **nameFromClassA;
-(id)initWithAPointer:(NSString **)name;
// ClassB.m
#implementation ClassB
#synthesize nameFromClassA;
-(id)initWithAPointer:(NSString **)name{
*nameFromClassA = *name;
}
//Print the name
-(void)printName{
NSLog(#"Name: %#", *nameFromClassA);
}
//Will this change the name in class A?
-(void)changeNameInClassA:(NSString* newName){
*nameFromClassA = newName;
}
Please, do not use double pointers here. You shouldn't handle things like that.
This is a simpler approach:
In the ClassA instance:
-(void)passAParameter{
NSString *name = #"Cyprian";
ClassB *classB = [[ClassB alloc] initWithAPointer:name];
...
}
While you define ClassB this way:
ClassB.h:
#interface ClassB{
NSString *nameFromClassA;
}
#property(nonatomic,retain) NSString *nameFromClassA; // Retaining it will give you less headaches
-(id)initWithAPointer:(NSString *)name;
#end
ClassB.m:
#implementation ClassB
#synthesize nameFromClassA;
// You should release all retained object when deallocating self
- (void) dealloc {
[nameFromClassA release];
nameFromClassA = nil;
[super dealloc];
}
-(id)initWithAPointer:(NSString *)name{
if ((self = [super init])) { // Always init the object from super!
self.nameFromClassA = name; // Retain the object calling self.
}
return self;
}
//Print the name
-(void)printName{
NSLog(#"Name: %#", nameFromClassA);
}
//Will this change the name in class A?
-(void)changeNameInClassA:(NSString* newName){
self.nameFromClassA = newName; // Retain it calling self.
}
#end
The assignment in your initWithAPointer: method should be just:
nameFromClassA = name;
That said, this code pattern smells of a bad design. What high-level goal is it that you're trying to accomplish?

Objective-C class_conformsToProtocol() bug?

I have come across some strange behavior in my iPhone Objective-C app.
I am using some code to test an object:
if (!class_conformsToProtocol([someVar someFunctionThatReturnsAClass], #protocol(MyProtocol)))
[NSException raise:#"Invalid Argument" format:#"The variables returned by 'someFunctionThatReturnsAClass' Must conform to the 'myProtocol' protocol in this case."];
Oddly, when I have a class that looks like this:
#interface BaseClass : NSObject<MyProtocol>
...
#end
#interface SubClass : BaseClass
...
#end
And when I call this fragment: class_conformsToProtocol([SubClass class], #protocol(MyProtocol)), it returns NO.
Also, this code fails:
class_conformsToProtocol([NSString class], #protocol(NSObject)); // also returns NO
While this code returns YES:
[NSString conformsToProtocol:#protocol(NSObject)];
Is there anything I am missing in the docs?
Or is this a bug of some sort? (I am on iOS 4.2 if that matters any).
If there's a bug here, it's in the documentation.
According to the source, class_conformsToProtocol() uses class_copyProtocolList() and then tests each resulting protocol against the parameter. class_copyProtocolList() is documented as only returning protocols that the given class adopts, but not protocols adopted by superclasses. class_conformsToProtocol() therefore only tests if the given class adopts a protocol and not if its superclasses do.
The documentation bug is that class_conformsToProtocol() doesn't state this behavior. However, the documentation does state that you should generally not use that function, but instead use NSObject's conformsToProtocol: method instead.
Use NSObject's conformsToProtocol: method.
Here's an experiment I tried:
#protocol MyProtocol
- (void) doSomething;
#end
#interface MyClass : NSObject<MyProtocol>
{
}
#end
#implementation MyClass
- (void) doSomething {
}
#end
#interface MyOtherClass : MyClass
{
}
#end
#implementation MyOtherClass
- (void) doSomething {
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MyClass *obj_one = [MyClass new];
BOOL one_conforms = [obj_one conformsToProtocol:#protocol(MyProtocol)];
MyOtherClass *obj_two = [MyOtherClass new];
BOOL two_conforms = [obj_two conformsToProtocol:#protocol(MyProtocol)];
NSLog(#"obj_one conformsToProtocol: %d", one_conforms);
NSLog(#"obj_two conformsToProtocol: %d", two_conforms);
[pool drain];
return 0;
}
Output:
obj_one conformsToProtocol: 1
obj_two conformsToProtocol: 1
Whereas:
MyOtherClass *obj_two = [MyOtherClass new];
BOOL conforms_two = class_conformsToProtocol([obj_two class], #protocol(MyProtocol));
NSLog(#"obj_two conformsToProtocol: %d", conforms_two);
Output:
obj_two conformsToProtocol: 0
Verdict:
This is a bug with class_conformsToProtocol, use the conformsToProtocol: method of NSObject
Unlike class_conformsToProtocol, NSObject's conformsToProtocol: method will check superclasses as well.

Category-like extension for instance variables

Is there a way to somehow emulate category behavior for a class regarding to it's instance variables, not methods ?
I have a ClassA, and I want to keep its name after extending it with new methods AND ivars from other cllass (ClassB).
Of course, I can inherit ClassA, but resulting class will have different name.
For methods addition, it's not a problem - category would be a good solution.
UPDATE: ClassA used as file owner for a XIB, and these fields to be extended are IBOutlets. So I need them at build phase.
Since the iPhone uses the modern Objective-C runtime, you can use associative references to add data to instances without having to declare instance variables. See the documentation for objc_setAssociatedObject etc.
If you wrap the calls to the runtime in standard accessor methods, it will be very easy to use.
I've investigated this question playing around associative references (thanks to Ole), with methods static variables, methods swizzling, and finally come to this simple solution (no runtime stuff). I simply use "categorized" class only to return a pointer to a derived class, which of course can contain additional ivars. Doing so I achieve one unexpected benefit: I can call super's class methods, which is impossible when extending through categories.
Example of a class extension (tested):
ClassA+ClassB.h
#protocol _ClassB_Protocol
#optional // to avoid warnings
- (IBAction) onClick:(id)sender;
#property (nonatomic, retain) IBOutlet UIButton *aButton;
#end
#interface ClassA (_ClassA_Category) <_ClassB_Protocol>
#end
#interface ClassB: ClassA <_ClassB_Protocol> {
UIButton *aButton; // _ivar_ to add
}
#end
ClassA+ClassB.m
#implementation ClassA (_ClassA_Category)
// this will be called first on [ClassA alloc] or [ClassA allocWithZone:(NSZone *)zone]
+(id) alloc {
if ([self isEqual: [ClassA class]]) {
return [ClassB alloc];
} else {
return [super alloc];
}
}
#end
#implementation ClassB: ClassA
#synthesize aButton;
-(void) dealloc {
[aButton release];
[super dealloc]; // this is impossible for an ordinary category
}
- (void) onClick:(id)sender {
// some code here
}
#end
Now we have in the same time:
ClassB "extends" ClassA (category way);
ClassB inherits ClassA (ClassB can call ClassA methods);
ClassB can be accessed through ClassA name (category way)
I put Martin's example into a trivial app replacing ClassA with NSData, ClassB with XXData, and onClick with getIvar, and invoked it (Mac OS X 10.6.6, Xcode 4 Final) with:
NSData * data = [NSData data];
NSLog(#"%#", [data getIvar]);
It fails with "-[NSConcreteData getIvar]: unrecognized selector sent to instance" ..
It fails because "alloc" in the NSData category (which returns the pointer to the derived class) is not called by the above code. If, instead, "alloc" is called explicitly, as in:
NSData * data = [[NSData alloc] init];
NSLog(#"%#", [data getIvar]);
then all is well.