iPhone SDK: Accessing methods in other classes - objective-c

In my iPhone application I have multiple class files, I have my main application's class files, then I have my UIView class files. I have a simple -(void) method declared in my UIView class files, how can I access it from my main applications class files?
A bit more detail: In my application a video is played, when this video finishes playing a notification is sent and actions are preformed, which I have already successfully set up, however when the movie finishes I would like a method declared in another class file to be preformed. If the method was declared in the same class file I would simply use this code: [self mySimpleVoidMethod]; But obviously this doesn't work If the method is declared in a different class file. I believe it is possible to access a method declared in a different class file, but I just haven't got a clue about how to do it. Sorry if I'm using completely incorrect terms to name things. But I am relatively new to programming all together.

You've got a couple of options, depending on your setup. Here are a few:
1) Add a reference to the class with the function (the callee) as a property in the caller's class:
Caller.h
#interface Caller : SomeObject {
Callee *myCallee;
...
}
#property(nonatomic, retain) Callee *myCallee;
Caller.m
#synthesize myCallee;
-(void)someAction {
[myCallee doSomething];
}
Something that sets up Caller after initializing both classes:
caller.myCallee = callee;
2) Use another notification event, like it looks like you already know how to do.
3) Use a protocol if you've got a bunch of different classes that Caller might need to call that all support the same method:
DoesSomething.h
#protocol DoesSomething
-(void)doSomething;
#end
Callee.h
#interface Callee : NSObject<DoesSomething> { // NSObject or whatever you're using...
...
}
-(void)doSomething;
Caller.h
#interface Caller : SomeObject {
id<DoesSomething> *myCallee;
...
}
#property(nonatomic, retain) id<DoesSomething> *myCallee;
... Then as per example 1.
4) Use performSelector to send a message to the class.
Caller.h
#interface Caller : NSObject {
SEL action;
id callee;
}
-(void)setupCallbackFor:(id)target action:(SEL)callback;
Caller.m
-(void)setupCallbackFor:(id)target action:(SEL)callback {
callee = target;
action = callback;
}
-(void)someAction {
if([callee respondsToSelector:action]) {
[callee performSelector:action];
}
I'm sure there are other ways, and there are pros and cons to each of these, but something in there should fit your needs and/or give you enough to scan the documentation to fill in any gaps...

I did a blog post a few weeks ago that outlines one way to do this. It is similar to the previous answers, and includes some sample code you can download and look at. It is based on using table view controllers, but you should be able to adapt the ideas to your application without too much difficulty.
Passing values and messages between views on iPhone

You'll need an instance of the other class, accessible from the code that runs when the movie finishes. Often, this is accomplished by storing an instance of the other class as a field in the class, set either via a "setter", or during construction. You could also use key-value observing, watching a key representing the playstate of the movie; an instance of the other class can register to observe the changes to this key.
Specifically for patterns using UIView, your UIViewController for the view will have access to it (through the view method). If your "main application's class files" have a pointer to the controller - which they probably will, setup via Interface Builder - then that's an easy way to get to a UIView instance.

Related

Prevent instantiation of readonly objects from framework

I am currently in the process of creating an objective c framework for iOS to help facilitate the interaction between an API and a developer. As part of this, I return various arrays of readonly objects that a developer can use to display information to the user. However, I would like to ensure that the objects displayed to the user come only from the framework and can not be instantiated by the developer using the framework.
My current implementation uses a custom constructor initializer that takes JSON from the api to instantiate itself. The only way that I am aware of accessing my custom constructor initializer is by putting its definition in the header file which makes it not only accessible to myself, but also the developer. I am aware that I can throw an inconsistency exception when the user tries to use the default constructor initializer, -(id)init;, but I can not stop them from creating their own JSON string and calling my custom constructor initializer.
Am I taking the correct approach to securing my private framework from interference from the developer using it? How else can I get around this to ensure the validity of data in these objects?
Source: Is it possible to make the -init method private in Objective-C?
You are correct that Objective-C doesn't allow for truly private methods by it's very nature, due to its dynamic dispatch system. However, assuming your question is not about true security, rather simply making it difficult to use the framework in an incorrect way, you have a few options.
A simple, common solution would be to put the declarations for methods you don't want to expose publicly in a category in a separate header file. You can still put these methods' implementations in the main implementation file for the class. So, a header with something like this:
// MyClass+Private.h
#interface MyClass (Private)
- (void)aPrivateMethod;
#end
Then, in your own source files where you need to access those private methods, you simply import MyClass+Private.h.
For a framework, you can set each header file to be Public or Private. Private headers will not be copied into the framework bundle, and therefore won't be visible to users of the Framework. You do this by opening the Utilities pane in Xcode (the right-side slide out pane), selecting the header in question, then choosing Private in the second column of the relevant row under "Target Membership".
Based on Andrew Madsen's solution, I ended up using was to have two different header files for each object; One that was public, and one that was private. The public header contains only the information needed by the developer to access the read only properties. Then my private header imports the public header and also contains a category with all the method calls I need to use within the SDK (including the initializer). I then import the private header into my implementation. The structure looks like this:
Public Header MyObject.h
#import <Foundation/Foundation.h>
#interface MyObject : NSObject
#property (nonatomic, retain, readonly) NSString *myValue;
#end
Private Header MyObject+Private.h
#import "MyObject.h"
#interface MyObject (Private)
+(MyObject*)MyObjectFromJSONString:(NSString*)JSONString;
-(id)initWithJSON:JSONString:(NSString*)JSONString
#end
Private Implementation MyObject.m
#import "MyObject+Private.h"
#implementation MyObject
#synthesize myValue = _myValue; //_myValue allows local access to readonly variable
- (id)init {
#throw [NSException exceptionWithName:NSInternalInconsistencyException reason:#"-init is not a valid initializer for the class MyObject" userInfo:nil];
return nil;
}
+(MyObject*)MyObjectFromJSONString:(NSString*)JSONString;
{
return [[MyObject alloc]initWithJSON:JSONString];
}
-(id)initWithJSON:JSONString:(NSString*)JSONString
{
self = [super init];
if(self){
//parse JSON
_myValue = JSONString;
}
return self;
}

Link class properties to UIControls values in Objective-C

basicaly I am a C# developer but started learning Objective-C couple of last days.
Now I have to do an exercise which need to create a class and link instance variables (properties) to the UIControls values of the View (e.g. UITextField string value).
Meaning I have already implemented the desired IBOutlets in the ViewControler and inside this controler I will create an instance of the created class. In C# a class could implement the INotifyPropertyChanged interface, bind the class to the controls and notify the object when the Datasource value has changed.
Is there anything equal to this concept in Objective C? Or how can I achieve something like that, only through events when value changed for every Control?
Thank you.
Your question and grammar is a bit ambiguous but it seems to me what you want is custom (manual) property getters/setters. Try this:
#intrface AClass: NSObject {
int iVar;
}
#property (nonatomic, assign) int iVar;
#end
#implementation AClass
- (int)iVar
{
// notify object of value being read, then:
return iVar;
}
- (void)setIVar:(int)_iVar
{
iVar = _iVar;
// then notify object about property being set
}
#end
Not 100% sure what you're asking for from your question. Are you asking whether the ViewController views can auto-update when your model changes?
There are a bunch of different mechanisms for providing notifications between objects/classes/etc. The main ones are as follows (I've included IBAction which you probably know for completeness):
1) IBAction - For UI controls just as you've connected IBOutlets in your UIViewController class, you can also fire events (touch up/touch down/etc) on user interaction.
2) NSNotification - you can post these pretty much anywhere:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003701
3) Key-Value Observing:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i

Swizzling a single instance, not a class

I have a category on NSObject which supposed to so some stuff. When I call it on an object, I would like to override its dealloc method to do some cleanups.
I wanted to do it using method swizzling, but could not figure out how. The only examples I've found are on how to replace the method implementation for the entire class (in my case, it would override dealloc for ALL NSObjects - which I don't want to).
I want to override the dealloc method of specific instances of NSObject.
#interface NSObject(MyCategory)
-(void)test;
#end
#implementation NSObject(MyCategory)
-(void)newDealloc
{
// do some cleanup here
[self dealloc]; // call actual dealloc method
}
-(void)test
{
IMP orig=[self methodForSelector:#selector(dealloc)];
IMP repl=[self methodForSelector:#selector(newDealloc)];
if (...) // 'test' might be called several times, this replacement should happen only on the first call
{
method_exchangeImplementations(..., ...);
}
}
#end
You can't really do this since objects don't have their own method tables. Only classes have method tables and if you change those it will affect every object of that class. There is a straightforward way around this though: Changing the class of your object at runtime to a dynamically created subclass. This technique, also called isa-swizzling, is used by Apple to implement automatic KVO.
This is a powerful method and it has its uses. But for your case there is an easier method using associated objects. Basically you use objc_setAssociatedObject to associate another object to your first object which does the cleanup in its dealloc. You can find more details in this blog post on Cocoa is my Girlfriend.
Method selection is based on the class of an object instance, so method swizzling affects all instances of the same class - as you discovered.
But you can change the class of an instance, but you must be careful! Here is the outline, assume you have a class:
#instance MyPlainObject : NSObject
- (void) doSomething;
#end
Now if for just some of the instances of MyPlainObject you'd like to alter the behaviour of doSomething you first define a subclass:
#instance MyFancyObject: MyPlainObject
- (void) doSomething;
#end
Now you can clearly make instances of MyFancyObject, but what we need to do is take a pre-existing instance of MyPlainObject and make it into a MyFancyObject so we get the new behaviour. For that we can swizzle the class, add the following to MyFancyObject:
static Class myPlainObjectClass;
static Class myFancyObjectClass;
+ (void)initialize
{
myPlainObjectClass = objc_getClass("MyPlainObject");
myFancyObjectClass = objc_getClass("MyFancyObject");
}
+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy
{
object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass);
}
Now for any original instance of MyPlainClass you can switch to behave as a MyFancyClass, and vice-versa:
MyPlainClass *mpc = [MyPlainClass new];
...
// masquerade as MyFancyClass
[MyFancyClass changeKind:mpc fancy:YES]
... // mpc behaves as a MyFancyClass
// revert to true nature
[MyFancyClass changeKind:mpc: fancy:NO];
(Some) of the caveats:
You can only do this if the subclass overrides or adds methods, and adds static (class) variables.
You also need a sub-class for ever class you wish to change the behaviour of, you can't have a single class which can change the behaviour of many different classes.
I made a swizzling API that also features instance specific swizzling. I think this is exactly what you're looking for: https://github.com/JonasGessner/JGMethodSwizzler
It works by creating a dynamic subclass for the specific instance that you're swizzling at runtime.

How to use delegate to pass data backwards/up view controller chain in Obj-C/Cocoa

I know there is plenty on the subject of delegates in the Apple dev documentation, as well as other books I have, and in resources like stackoverflow and others. But I'm still not getting it.
I recently watched the lecture on Navigation View Controllers, etc. in Stanford's CS193P Winter 2010 series, and in that lecture they talk about passing data forward on a stack of view controllers, which is easy. But they made a brief mention that you'd ideally use a delegate/protocol to pass data "backwards" (from detail view controller to list view controller, for example), but they didn't do a demo or post sample code.
I've read and searched for a sample of this exact scenario so I can wrap my head around that use of delegate/protocol, but can't find it. Here's some pseudo-code I'm playing with. Should it achieve passing the data "backwards"?
myListController : UIViewController <SetDataInParent> {
// when pushing detail controller onto stack,
// set DetailController delegate = self
}
myDetailController : UIViewController {
//header file
#protocol SetDataInParent <NSObject>
- (void)willSetValue:(*NSString);
#end
#interface myDetailController {
id <SetDataInParent> delegate;
}
#end
// class/m file
#implementation
#synthesize delegate;
- (void)willSetValue:(*NSString) {
// code here that would take argument
// from detail controller and set
// a value or text field to that
// argument in list controller
}
// send message to list controller class
- [delegate willSetValue:string];
#end
}
A delegate is simply any other Objective-C instance other than the one you are currently to which you will be sending one or more messages. The type you specified (id <SetDataInParent) reads that it's any Objective-C type that conforms to the SetDataInParent protocol (which I don't see defined in your code, but you could have it elsewhere.)
When going "backwards" typically the delegate link between the two objects is made at the time the child view is created. Therefore at the point the list view controller creates the detail view controller the former should set the delegate in the latter to self. The detail view controller then can use that delegate pointer to send messages to the list view controller, either directly via e.g., willSetValue: or indirectly (via performSelector:withObject:.) When using performSelector:withObject: it is generally a good idea to call respondsToSelector: on the delegate first to make sure you won't throw an exception should the object not respond to that message.

Is it possible to declare a method as private in Objective-C?

Is it possible to declare a method as private in Objective-C?
If you're working in Objective-C 2.0, the best way to create methods that are "hard" for others to call is to put them in a class extension. Assuming you have
#interface MyClass : NSObject {
}
- (id)aPublicMethod;
#end
in a MyClass.h file, you can add to your MyClass.m the following:
#interface MyClass () //note the empty category name
- (id)aPrivateMethod;
#end
#implementation MyClass
- (id)aPublicMethod {...}
- (id)aPrivateMethod {...} //extension method implemented in class implementation block
#end
The advanage of a class extension is that the "extension" methods are implemented in the original class body. Thus, you don't have to worry about which #implementation block a method implementation is in and the compiler will give a warning if the extension method is not implemented in the class' #implementation.
As others have pointed out, the Objective-C runtime will not enforce the privateness of your methods (and its not too hard to find out what those methods are using class dump, even without the source code), but the compiler will generate a warning if someone tries to call them. In general, the ObjC community takes a "I told you not to call this method [by putting it in a private class extension or category or just by documenting that the method is private] and you called it anyways. Whatever mess ensues is your fault. Don't be stupid." attitude to this issue.
No, any object can send any message to any other object. You can, however, put the method in a category that's part of the class's implementation file. That way, you'll get a "Class may not implement this method" warning if you try to call it anywhere else. That's the normal way of making a method "private."
There is nothing that will prevent the method being called (since objective-c is message based anything can be sent any message), but you can declare them outside of the header so they are not visible and the compiler will generate warnings if used.
This works for both class and instance methods.
E.g.
#import "SomeClass.h"
// Interface for hidden methods
#interface SomeClass (hidden)
+(void) hiddenClassMethod;
-(void) hiddenInstanceMethod;
#end
Note: Do NOT declare variables like this or they will become class-variables - e.g. only one variable will be used by all instances.
You can do so by using categories. I've got a fuller description in my answer to this SO question.
As has been said, you can't stop anyone sending a message to a selector, but by using categories you can reduce the visibility of these functions.
Also, you can have more than one category extending a class. So, by using informative category names you can group private functions into related blocks, improving the self-documenting nature of your code.
As others mentioned, you can't have code that's
a method, and
impossible to call from outside a class.
Folks have already pointed out that you can abandon point 2, and get a method that's hard-but-not-impossible to call. Alternatively, why not abandon point 1?
static id myPrivateMethod(MyObject *me, int arg1, id arg2) { ... }
Now the code can only be called from within same file. You don't get any of the magic private-member access you can get with a method, so this is by no means a perfect solution. But there's no better way to achieve privacy.
To implement hidden methods (instance and/or class)
// ===========================
// = File: SomeClass.m
// ===========================
#import "SomeClass.h"
// =================================
// = Interface for hidden methods
// =================================
#interface SomeClass (hidden)
-(void) hiddenInstanceMethod;
#end
// ================================
// = Implementation for SomeClass
// ================================
#implementation SomeClass
-(void) hiddenInstanceMethod
{
printf( "Hidden instance method\n" );
}
-(void) msg
{
printf("Inside msg()...\n");
[self hiddenInstanceMethod];//private method calling
}
#end
http://macdevelopertips.com/objective-c/private-methods.html
reffer this link it will be helpful .