Returning/Passing a reference as its protocol - iphone

Given
#protocol Person<NSObject>
#interface Greek : NSObject <Person>
Can you define a method as
+(id<Person>)newGreek{
return [[Greek alloc] init];
}
and use the return type as
id<Person> person = [Persons newGreek];
Or call a method
Greek *greek = [[Greek alloc] init];
[self talk:greek];
which is defined as
-(void)talk:(id<Person>)person
If not, what are the alternatives?

Yes you can. In Protocols in the Objective-C Programming Language, the section on Type Checking specifically shows the first form, and the second works as well, even though it is not listed on that page. The protocol declaration (<Person>) is a type modifier which can be used on any objective-c object, meaning you can use it wherever you define the type of the object.

Related

Making Xcode complain about a missing parameter

I am designing a new application by modernizing code I wrote in the past. This old code uses the class/delegate model and I am trying to transform them to use blocks as callbacks, not the delegate stuff.
What I do is to create a property like
#property (nonatomic, copy) void (^onTouch)(NSInteger index);
That would pass to the object using that class a block where code can be inserted and in this case executed on touch.
But my problem is this. When you use delegates and you have a method on the delegate protocol, Xcode will warn if you use that class and forget to implement the delegate protocols. Is that a way to do that with blocks? Or in other words: is there a way to make Xcode complain if a callback block is not defined by the caller?
I mean this would be the correct:
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = ^(NSInteger *index){ //call back code to be executed };
This would be OK too
MyClass *obj = [[MyClass alloc] init];
obj.onTouch = nil;
but this would generate a message
MyClass *obj = [[MyClass alloc] init];
// no callback block defined.
Is this possible?
If you want to enforce setting a certain parameter, I would include it in the initializer.
MyClass *obj = [[MyClass alloc] initWithBlock:^(NSInteger *index) { /* code*/ }];
Then, in MyClass:
- (id)init {
// This will result in a runtime error if you use the wrong initializer.
NSAssert(NO, #"Use initWithBlock instead.");
}
- (id)initWithBlock(initWithBlock:^(NSInteger *)block) {
self = [super init];
if (self) {
self.onTouch = block;
}
return self;
}
Also note, attempting to execute a NULL block results in a crash, so make sure to do:
if (self.onTouch) { self.onTouch(); }
Wherever you run the block.
First, I strongly recommend defining types to represent your blocks - makes them a lot easier to work with, especially if you need to refactor the parameters.
You can't write code that distinguishes between "I set this property to nil" or "the runtime initialized this property to nil", at least not without some crazy runtime code to check the stack. Only option I can think of would be to use the null object pattern. Before I elaborate, bear in mind that I haven't actually tried to test this, but it should work. Define a block that means 'has no value' and set your property to point to that block on init. Then you can compare to that NullBlock at runtime to identify if someone explicitly set the property to nil (because it would be nil at that point) or gave it a real non-nil value.
Alternatively, if you don't mind manually writing your set accessors, you could have a BOOL that tracks if someone set the property explicitly. Then when you call the block just check if someone actually set the value or not.
#synthesize onTouchBlock=_onTouchBlock;
MyBlock _onTouchBlock;
BOOL _onTouchBlockWasSet;
- (void)setOnTouchBlock:(MyBlock)block {
_onTouchBlockWasSet = YES;
_onTouchBlock = block;
}
I would not recommend passing the value in the initializer because that makes it tied to the creation of that object type. If you wanted to change the block in code based on some condition, you'd be back to square one. Also, it prevents you from using storyboards which create that object.

Using NSMutableDictionary as backing store for properties

I am looking for a shorthand way of setting my properties directly to an NSMutableDictionary that is a instance variable. ie:
KVCModle.h:
#interface KVModel : NSObject {
NSMutableDictionary * data;
}
#property(nonatomic,assign)NSString * string1;
#property(nonatomic,assign)NSString * string2;
#end
KVCModel.m
#import "KVModel.h"
#implementation KVModel
-(id)init
{
self = [super init];
if(self)
{
data = [[NSMutableDictionary alloc] init];
}
return self;
}
-(NSString *)string1
{
return [data objectForKey:#"string1"];
}
-(NSString *)string2
{
return [data objectForKey:#"string2"];
}
-(void)setString1:(NSString *)_string1
{
[data setObject:_string1 forKey:#"string1"];
}
-(void)setString2:(NSString *)_string2
{
[data setObject:_string2 forKey:#"string2"];
}
-(void)dealloc
{
[data release];
[super dealloc];
}
#end
I have tried to override setValue:ForKey: and valueForKey:, but those aren't called, they allow you to directly set properties without using the property syntax.
I have made preprocessor macros to make this work in the past, but I am not interested in typing at all, and would like to avoid as much of it as I can in the future. Is there a way to make this work that I am not familiar with?
I have thought about using NSManagedObject, but I am not sure if I can get what I want out of that.
EDIT:
source
If you're trying to access the properties with code like foo = obj.foo and obj.foo = foo, that's why it doesn't work.
Property-access syntax is synonymous with message syntax; the former is exactly the same as foo = [obj foo], and the latter is exactly the same as [obj setFoo:foo]. There is no KVC code to intercept. Properties are at the language level; KVC is at the framework level.
You'll need to intercept the accessor messages instead. Consider implementing the resolveInstanceMethod: class method, in which you “resolve” the selector by adding a method implementation to the class using the Objective-C runtime API. You can add the same implementation(s) for many different selectors.
For your purpose, have a function or method that examines the selector (using NSStringForSelector and regular NSString-examining techniques) and returns two facts: (1) the property name, and (2) whether it's a getter (foo, isFoo) or setter (setFoo:). Then, have two more methods, one a dynamic getter and the other a dynamic setter. When the selector names a getter, add it with your dynamic-getter method; when the selector names a setter, add it with your dynamic-setter method.
So how do the dynamic-getter and -setter methods work? They'll need to know what property to dynamically get and set, but they also need to take no arguments (getter) or one argument (setter, which takes the value), in order to match the original property-access message. You might be wondering how these generic implementations can know what property to get or set. The answer is: It's in the selector! The selector used to send the message is passed to the implementation as the hidden argument _cmd, so examine that selector the same way as before to extract the name of the property you should dynamically get or set. Then, the dynamic getter should send [data objectForKey:keyExtractedFromSelector] and the dynamic setter should send [data setObject:newValue forKey:keyExtractedFromSelector].
Two caveats:
You may still get complaints from the compiler when you use the property-access syntax to access a “property” that you have not declared in the class's #interface. This is normal and intentional; you're really only supposed to use property-access syntax to access known formal properties. What you're doing, while I found it fun to solve, is technically an abuse of the property-access syntax.
This will only work for object values. KVC does the boxing and unboxing for primitive values, such as integers; since KVC is not involved, no free boxing and unboxing. If you have declared formal properties (see 1), you'll need to introspect them using the Objective-C runtime API, and do the boxing and unboxing yourself with your findings.
This piqued my curiosity, so I went ahead and used Peter Hosey's suggestion of overriding +resolveInstanceMethod: to generate the getters and setters. I posted the resulting object (DDDynamicStorageObject) to a github repository:
https://github.com/davedelong/Demos
What you basically want is your own implementation of the NSManagedObject machinery. I have done something similar. Look here: https://gist.github.com/954035 HTH
(Updated the code to remove the dependency on the non-existant NSString+Utilities.h)
(Added missing ReleaseAndZero() macro)
For the love of all that is sacred - do not use an NSDictionary as a place to stuff every conceivable property of a model object. Ivars are easier to debug, and much much clearer to other developers (including your future self).
If you want to use a dictionary, use a dictionary and some statically defined keys - but if you want a model object, use some ivars
I come to the same problem today just like you. So I find your question posted here.
The answers above used the +resolveInstanceMethod: is a little bit hard for me. :)
My understanding is that as long as we setup the property, we would have getter and setter method, so I use the setter method to implement it.
BDLink.h
#property (nonatomic, strong) NSString *type;
#property (nonatomic, strong) NSString *displayName;
#property (nonatomic, strong) NSString *linkURI;
BDLink.m
- (id)initWithLinkInfoDictionary:(NSDictionary *)linkInfoDict {
for (NSString *key in linkInfoDict) {
const char *rawName = [key UTF8String];
NSString *setMethodString = [NSString stringWithFormat:#"set%c%s:", toupper(rawName[0]), (rawName+1)];
SEL setMethod = NSSelectorFromString(setMethodString);
if ([self respondsToSelector:setMethod]) {
[self performSelector:setMethod withObject:linkInfoDict[key]];
}
}
return self;
}
Hope it would be helpful. My first answer, :)

How do I call methods in a class that I created dynamically with NSClassFromString?

The reason I am doing dynamic class loading is because I am creating a single set of files that can be used across multiple similar projects, so doing a #import and then normal instantiation just won't work. Dynamic classes allows me to do this, as long as I can call methods within those classes. Each project has this in the pch with a different "kMediaClassName" name so I can dynamically load different classes based on the project I'm in:
#define kMediaClassName #"Movie"
Here is the code I am using to get an instance of a class dynamically:
Class mediaClass = NSClassFromString(kMediaClassName);
id mediaObject = [[[mediaClass alloc] init] autorelease];
Then I try to call a method within that dynamic class:
[mediaObject doSomething];
When I then type this into Xcode, the compiler shows a warning that the class doesn't have this method, even though it does. I can see it right there in my Movie.h file. What is going on? How do I call a method from a dynamically instantiated class?
And what if I need to pass multiple arguments?
[mediaObject loadMedia:oneObject moveThe:YES moveA:NO];
Thanks for the help in advance.
you can declare a protocol, like so:
#protocol MONMediaProtocol
/*
remember: when synthesizing the class, you may want
to add the protocol to the synthesized class for your sanity
*/
- (BOOL)downloadMediaAtURL:(NSURL *)url toPath:(NSString *)path loadIfSuccessful:(BOOL)loadIfSuccessful;
/* ...the interface continues... */
#end
in use:
Class mediaClass = NSClassFromString(kMediaClassName);
assert(mediaClass);
id<MONMediaProtocol> mediaObject = [[[mediaClass alloc] init] autorelease];
assert(mediaObject);
NSURL * url = /* expr */;
NSString * path = /* expr */;
BOOL loadIfSuccessful = YES;
BOOL success = [mediaObject downloadMediaAtURL:url toPath:path loadIfSuccessful:loadIfSuccessful];
Well it might be there, but the Compiler doesn't know about it because it assumes that mediaClass is just some Class object, but nothing specific. NSClassFromString() is a runtime function and thus can't give the compiler a hint at compile time about the object.
What you can do:
Ignore the warning
Use [media performSelector:#selector(doSomething)];
And btw, this is wrong:
Class mediaClass; = NSClassFromString(kMediaClassName);
it should be:
Class mediaClass = NSClassFromString(kMediaClassName);
An easier and fancier solution than NSInvocation :)
Class mediaClass = NSClassFromString(kMediaClassName);
if(mediaClass){
id mediaObject = class_createInstance(mediaClass,0);
objc_msgSend(mediaObject, #selector(doSomethingWith:andWith:alsoWith:), firstP, secondP,thirdP);
}
Explanation:
class_createInstance(mediaClass,0); does exactly the same as [[mediaClass alloc] init];
if you need to autorelease it, just do the usual [mediaObject autorelease];
objc_msgSend() does exactly the same as performSelector: method but objc_msgSend() allows you to put as many parameters as you want. So, easier than NSInvocation right? BTW, their signature are:
id class_createInstance(Class cls, size_t extraBytes)
id objc_msgSend(id theReceiver, SEL theSelector, ...)
For more info you can refer the Objective-C Runtime Reference
As Joe Blow says, NSInvocation will help you here, though NSObject has a couple of shortcut methods that you can use: -performSelector:, -performSelector:withObject:, and -performSelector:withObject:withObject:.

Declaring an object type in an if statement

I'm trying to declare a variable inside an if statement. If the result of a query is YES then the object will be of one type, otherwise it will be of another type. A bit like this...
if (YES) {
ObjectTypeA *object = [[ObjectTypeA] alloc] init];
}
else {
ObjectTypeB *object = [[ObjectTypeB] alloc] init];
}
Once that's done I want to use object with the same methods no matter what type it is. I tried declaring object as an id before the if statement but get an error: member reference type 'struct objc_object *' is a pointer; maybe you meant to use '->'?
I also tried declaring both to be separate objects outside the if and then make the pointer point to whichever it was once I knew. That wouldn't work either.
I know that the compiler is trying to protect me from myself by doing this but in this circumstance I need a way round it please.
Thanks.
The most common pattern for this problem in Cocoa/Cocoa Touch is to define a protocol.
A protocol is a collection of methods that can be implemented by any object.
If you make ClassA and ClassB conform to a protocol containing the methods you need them to respond to then you don't need to worry about which type of object you get.
The idea is "if it looks like a duck and quacks like a duck, then it's probably a duck".
You can use dynamic typing and create your objects depending on the outcome of your query, but ensure that the resulting object conforms to a particular protocol, like so:
id <MyProtocol> myObject;
if (YES)
myObject = [[ClassA alloc] init];
else
myObject = [[ClassB alloc] init];
[myObject myMethod];
[myObject release];
I think this code should work well:
id obj = nil;
if (YES) {
obj = [[ObjectTypeA] alloc] init];
} else {
obj = [[ObjectTypeB] alloc] init];
}
[obj performSelector:#selector(YOUR_METHOD) withObject:YOUR_OBJECT];
You want dynamic typing :)
The way around this is to declare a third object that both of these inherit from
OR
You could use the adapter pattern and create an object that accepts both of these objects as a member and then wrap the functions you wish to call into that object
good luck!
--> These are genral OO solutions; I'm not a Objective-C developer

Using an old OS category and a new OS informal protocol in the same code base

NSStreamDelegate was defined in previous OS as (NSObject)NSStreamDelegate
In the latest OS it is defined as id
Both have the same function.
If I want to write code that is Runtime system aware. How do I create an object that is both and neither? I dream of that truly universal app.
if (catchOS10.5_or_iOS3.2) {
[MyStream setDelegate:myObj]
} else {
[MyStream setDelegate:myObjWithProtocol]
}
I have myHandlerClass which in the NEW os is
MyClass:NSObject
Thus my quandary.
Any suggestions?
-A
Do you actually have trouble getting this to work on both versions? The two ideas are basically the same.
You will definitely have to declare your delegate class as implementing the NSStreamDelegate protocol (which is a formal protocol, not an informal one, in the current SDK):
#interface MyHandlerClass : NSObject <NSStreamDelegate> {
// ...
}
-(void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent;
#end
Since the "id" type is really just a typedef for a pointer to an Obj-C object, your pointer to your delegate class will both be an id as well as an NSObject:
NSStream *myStream = [[NSStream alloc] init];
MyHandlerClass *del = [[MyHandlerClass alloc] init];
myStream.delegate = del;
... should work on both SDKs. Or, if you're creating your stream inside of your delegate class (a common idiom) you'd do:
NSStream *myStream = [[NSStream alloc] init];
myStream.delegate = self;
to the same happy end.