I am using AFNetworking, I have a subclass created for AFHTTPSessionManager which works on iOS7, i would like to know it is possible that i can inherit the same subclass but with a different super class say AFHTTPRequestOperationManager based on some preprocessor directives.
Thanks
The "standard" way to deal with this would be to define an interface for your class functions, then create "old" and "new" classes to implement the interface. Use a "factory" to create the correct one for the runtime environment.
You could instead use a "wrapper" class instead of the interface -- one that creates the version-specific class internally and forwards calls to it.
Applying the use of a Factory (in the form of a class cluster)
note that code below is only an indication, it won't compile from a copy/paste
#interface MyHTTPClient
- (void) post:(NSURLRequest *)request params:(NSDictionary *)params
#end
#implementation MyHTTPClient
- (id) init {
if(iOS_version < 7) {
return [[_MyiOS7HttpClient] alloc] init];
} else {
return [[_MyiOS6HttpClient alloc] init];
}
}
- (void) post:(NSURLRequest *)request params:(NSDictionary *)params {
NSAssert(NO, #"private instances should respond to this");
}
#end
now the private instances, which are not visible from the project (only via MyHttpClient)
#import "MyHttpClient.h"
#implementation _MyiOS7HttpClient
- (void) post:(NSURLRequest *)request params:(NSDictionary *)params {
// use new ios7 session manager here to do stuff
}
#end
and create a similar class for MyiOS6HttpClient which uses none of the iOS7 Session management.
So the Class Cluster in this case gives you:
A Clean interface to either MyiOS7HttpClient or MyIos6HttpClient via MyHttpClient,
Other code does not need to know which instance you are actually using.
No Need for message forwarding.
You can't do this at compile-time. The compiler has no idea which of the supported iOS version(s) your app will run on. You may create several versions of the class, and create the correct one based on a runtime version check.
Related
I am trying to bootstrap Typhoon using the PList integration method but my ApplicationDelegate is being created twice. The first time it is created, it is obviously being created by Typhoon. That time, it uses the special initializer initWithAssembly: and Typhoon feeds it the assembly.
The second time, the time that matters, it is created using init. It never gets a reference to the assembly.
Just in case, I also injected the assembly via the property method. No go.
Here is the code:
Assembly
- (UIApplication *)sharedApplication {
return [TyphoonDefinition withClass:[UIApplication class] configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:#selector(sharedApplication)];
}];
}
- (CTISApplicationDelegate *)appDelegate {
return [TyphoonDefinition withClass:[CTISApplicationDelegate class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:#selector(initWithAssembly:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:#(3)];
}];
definition.scope = TyphoonScopeSingleton;
}];
}
AppDelegate
#property (nonatomic, strong, readwrite) ApplicationAssembly *assembly;
#property (nonatomic, strong, readwrite) UIWindow *window;
- (instancetype)initWithAssembly:(ApplicationAssembly *)assembly;
...
// This gets called once, the first time, and assembly is NOT nil.
- (instancetype)initWithAssembly:(ApplicationAssembly *)assembly {
self = [super init];
if (self) {
self.assembly = assembly;
}
return self;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// This gets ca
lled once (after second init) and self.assembly is nil.
AcceptDisclaimerAppInfoModule *disclaimer = [[self.assembly applicationInformationModuleAssembly] acceptDisclaimerModule];
[disclaimer launchModuleFromWindow:self.window];
[self.window makeKeyAndVisible];
return YES;
}
After looking online and going a bit crazy over-thinking this problem, I came to a number of conclusions.
The root of the problem is that Typhoon and my main.m entry point were not in sync in any form. So, main.m calls UIApplicationMain() and one of the arguments is a string that specifies the kind of id<UIApplicationDelegate> you want. I've never seen any deviation from this pattern so I was not willing to change that up.
Therefore, it is a given that the id<UIApplicationDelegate> is not going to be constructed via Typhoon in a way that is "built-into" the framework. And while you could do one of the following, I don't recommend any: they all seem wrong.
Instantiate an instance of your root TyphoonAssembly directly from your app delegate
Create a singleton IContainer object that can hug the TyphoonAssembly instance created on startup
Use categories with associated objects in an evil manner
The problem is... at some point, you're going to need to do one of these evil things no matter what if you don't get it right.
The reason is... Typhoon is clearly designed to work in the context of "object graphs," so the entire TyphoonAssembly and any connected assemblies can be thought of as webs of graphs. Once you get in the web, you're fine - you can take it from there. You just need to get in...
So, I decided to do as follows:
Create interfaces for each "object graph" of related objects I call IContainer, even if they spanned multiple assemblies or if were smaller than an assembly. This disconnected the idea of Typhoon from the IContainer and makes it possible to debug without Typhoon by substituting a mock IContainer in place.
Use constructor injection EXCLUSIVELY except in one very notable case - the one I had just mentioned, the app delegate. There, use property injection to inject just one property - the IContainer in question.
Whenever you use property injection, you might as well just inject a single property, the IContainer, because you've already broken encapsulation and you might as well make it easy on yourself.
Implement something fun to prove to yourself that Typhoon's default scope works they way you think it does. I implemented a few "alerts" whenever I detected multiple calls to any constructor in the same object graph.
Use id<nonatomic, weak> for delegate types, not id<nonatomic, assign> as I had done for the past year. Something about the way Typhoon works under the hood must make it constantly be letting go of delegates.
Use PList injection and Assembly Composition. An example:
In your Info.plist, add a key called TyphoonInitialAssemblies with Array type and values that are the class names of your assemblies. But...
Don't forget to do the other half, which is to make sure you have a "root" assembly like RootAssembly and then some ModuleAssemblys that are stored by the RootAssembly:
#protocol IAppLaunchContainer
- (UIWindow *)launchWindow;
- (UIViewController *)launchRootViewController;
- (UIImageView *)launchImageView;
#end
#protocol IDefaultUIComponentsContainer
- (UIView *)uiDefaultView;
- (UILabel *)uiDefaultLabelWithName:(NSString *)name;
- (UIButton *)uiDefaultButtonWithTitle:(NSString *)title;
#end
#interface RootAssembly : TyphoonAssembly<IAppLaunchContainer, IDefaultUIComponentsContainer>
#property (nonatomic, strong) SubAssemblyA *thisModuleAssembly;
#property (nonatomic, strong) SubAssemblyB *thatModuleAssembly;
#end
In this case, your Info.plist would have:
TyphoonInitialAssemblies (Array)
SubAssemblyA
SubAssemblyB
So this one has me stumped - probably something simple, but I'm clueless.
I'm defining a custom class, containing one method that receives one message (an integer). When calling that method, the compiler refuses to recognize the message I'm trying to send along with the call. ("No known class method for selector 'sendMessage:'. Removing the message from both the call and the definition - i.e. removing the :(int)mode from the definition, and the :1 from the call - allows it to compile fine (but then of course I lose the functionality).
I've tried defining it as an instance method, and as a class method - neither one works.
Many thanks in advance for your collective wisdom!
custom class "Communications.h":
#interface Communications : NSString
+(NSString*)sendMessage:(int)mode;
#end
Communications.m:
#import "Communications.h"
#interface Communications ()
#end
#implementation Communications
+(NSString*)sendMessage:(int)mode {
// Do something important
}
ViewController.h:
#import "Communications.h"
- (void) tapPanic:(UITapGestureRecognizer*)sender;
ViewController.m:
- (void) tapPanic:(UITapGestureRecognizer *)sender {
[Animations animatePanic:self.view type:0];
panicactive = 1;
NSString* tmpResponse = [Communications sendMessage:1];
UILabel* tmpServerResponsePanic = [self.view viewWithTag:10002];
tmpServerResponsePanic.text = tmpResponse;
[[self serverResponsePanic] setNeedsDisplay];
}
So, chalk it up to weirdness with Xcode... copy / pasting the contents of Communications .h and .m into new files, with a new class definition (Comms), did the trick. I think the compiler got confused and was remembering an old definition of the method.
Okay, so I was reading here declaring global variables in iPhone project and I noticed the line with this code: [[[UIApplication sharedApplication] delegate] myNSString];.
Basically, I want a user to input something in a text field on the flipside view, then have it stored in a variable which is accessed on the main view. Ideally, the main view would be able to just read the text field from the flipside view, but this seems to be impossible (I've spent several hours each day for the past few days scouring the web and various books for an answer about how to do this and no one seems to be able to give a definitive answer). Therefore, I'm resorting to using a global variable to tackle this.
Will the code that I printed above somehow allow me to do this? I've been trying to adapt it for the past hour, but have come up with nothing except No known instance method for selector 'myNSString' and I'm not quite sure what that means in this case.
Can someone please help me out? I feel like I can keep trying different things but without some sort of help, I'm just shooting in the dark here. Thank you!
You may want to think about using a singleton to hold your data if you're set on using a global variable. There's a good tutorial on singletons here: http://www.galloway.me.uk/tutorials/singleton-classes/ -basically it's a class that can be shared throughout the application and accessed/modified by different controllers. You'd be able to create a property on it, write to that property from the flip view, and then access that property from your main view.
#import "Singleton.h"
#implementation Singleton
#synthesize yourTextField;
#pragma mark Singleton Methods
+ (id)sharedManager {
static Singleton *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
yourTextField = #"";
}
return self;
}
You could call it in code by importing its header file and:
Singleton *mySingleton = [Singleton sharedManager];
the mySingleton object will have the text field attached. It can be accessed by:
mySingleton.yourTextField;
.h file:
#import <Foundation/Foundation.h>
#interface Singleton : NSObject
#property (nonatomic, strong) NSString *yourTextField;
+ (id)sharedManager;
#end
Singleton (remember about dispatch_once), static variables or NSUserDefaults. It really depends what you really need.
If you are using storyboards and just want to pass data between VC, then you can use "prepareForSegue" method (described here https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/prepareForSegue:sender:).
Segue has "destinationController" property, so you can setup VC before showing it.
I have a protocol like this :
#import <Foundation/Foundation.h>
#protocol StoreDisplayerDelegate <NSObject>
-(void) changeActionForObjectWithId:(NSString *)objectID ;
#end
and i have a callass with conforms to the precedent protocol StoreDisplayerDelegate
#interface ShelfVC : UIViewController :<StoreDisplayerDelegate>
....
#implementation ShelfVC
...
- (void)viewDidLoad {
...
DownloadManager *manager = [DownloadManager sharedInstance];
[manager setStoreDisplayerDelegate:self];
....
}
#pragma mark StoreDisplayerDelegate methods
-(void) changeActionForObjectWithId:(NSString *)objectID {
......
}
#end
And in my code ( in the same class) sometimes i am calling the delegate methods to do something form me, for example :
- (void)anOtherMethod{
[self changeActionForObjectWithId:nil];
}
My Questions
1. is : When object is a delegate for an other object, is the methods implemented by the delegate called only by the other object ( witch have a reference for it ) ? i mean by this, for example in the code i have shown should the methode changeActionForObjectWithId: just called by the downLoad manager or can i use it in the inernal of my class like this :
is what i am doing cleaning or bad design of using Delegate pattern ?
I hope that it is clear.
Your delegate method name sounds like a command.
-(void)changeActionForObjectWithId:(NSString *)objectID;
It sounds like your StoreDisplayer is telling delegate to do something. The fact that you are also tempted to call that method from within the ViewController confirms it.
That is not the delegate pattern. The delegate pattern is for a class to inform a delegate of a change, or to ask the delegate for some information. The delegating class (StoreDisplayer?) shouldn't know about what any particular delegate does, so it shouldn't be able to give it direct specific commands. Only delegate generic behaviour to it.
Delegate method look more like these examples:
-(BOOL)actionShouldChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer;
-(void)actionWillChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer objectId:(NSString *)objectId;
-(void)actionDidChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer objectId:(NSString *)objectId;
I'm not saying those are what you need, but they should give you the idea.
When your delegate methods look like this, clearly you will not be tempted to call them from anything other than the class that's doing the delegation (StoreDisplayer).
In my project I have the same situation for different purpose. In my opinion the delegate method must be called only by the other object, because it is its method.
If you need to do certain action in the delegate method, its better to create another private method to performing the action and call it from the delegate methods.
Some example. Instead of doing this:
- (void)anOtherMethod {
[self changeActionForObjectWithId:nil];
}
- (void)changeActionForObjectWithId:(NSString *)objectID {
< some actions >
}
I do this:
- (void)privateMethod{
< some actions >
}
- (void)anOtherMethod {
[self privateMethod];
}
- (void)changeActionForObjectWithId:(NSString *)objectID {
[self privateMethod];
}
Why do this? Because you have to think to the delegate methods to an "extension" to your base object: if you delete the "changeActionForObjectWithId" (becuase, for example, you don't need the delegate anymore after a refactoring) the code will continue to work.
A delegate is a protocol that allows an object to perform certain actions.
In your example:
#interface ShelfVC : UIViewController : <StoreDisplayerDelegate>
This tells the compiler that UIViewController is going to implement some methods in the StoreDisplayerDelegate protocol. How the object that applicable to the StoreDisplayerDelegate behave would depend on the protocol in the delegate methods.
You got a little confused with delegates and protocols. a delegate is a design-pattern wich uses a protocol.
a protocol only defines methods and properties expected to be implemented by another (unspecific) class. from where this methods/properties get accessed doesn't matter.
Im trying to make it so that every single UIControl in my application (UIButton, UISlider, etc) all have special extra properties that I add to them.
I tried to accomplish this by creating a UIControl Category and importing it where needed but I have issues.
Here is my code.
My setSpecialproperty method gets called but it seems to be getting called in an infinite loop until the app crashes.
Can you tell me what Im doing wrong or suggest a smarter way to add a property to all of my UIControls?
#interface UIControl (MyControl)
{
}
#property(nonatomic,strong) MySpecialProperty *specialproperty;
-(void)setSpecialproperty:(MySpecialProperty*)param;
#end
////////
#import "UIControl+MyControl.h"
#implementation UIControl (MyControl)
-(void)setSpecialproperty:(MySpecialProperty*)param
{
self.specialproperty=param;
}
///////////////
#import "UIControl+MyControl.h"
#implementation ViewController
UIButton *abutton=[UIButton buttonWithType:UIButtonTypeCustom];
MySpecialProperty *prop=[MySpecialProperty alloc]init];
[abutton setSpecialproperty:prop];
While you can't add an iVar to UIControl via a category, you can add Associated Objects, which can be used to perform much the same function.
So, create a category on UIControl like this:
static char kControlNameKey;
- (void) setControlName: (NSString *) name
{
objc_setAssociatedObject(self, &kControlNameKey, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *) controlName
{
return (NSString *)objc_getAssociatedObject(array, &kControlNameKey);
}
There's more to it than that, I guess you'll need to check if an association exists before setting a new one, otherwise it will leak, but this should give you a start.
See the Apple Docs for more details
self.specialproperty=param is exactly the same as calling [self setSpecialproperty] (see here for some totally non biased coverage of Obj-C dot notation), which makes your current usage infinitely recursive.
What you actually want to do is:
-(void)setSpecialproperty:(MySpecialProperty*)param
{
_specialproperty = param;
}
Where _specialproperty is the implicitly created ivar for your property.
I'm assuming there's some reason why you've implemented your setSpecialproperty setter? Why not just use the one that is implicitly created for you?
the problem is that you can not add a property to a category, you can add behavior (methods) but not properties or attributes, this can only be done to extensions, and you can not create extensions of the SDK classes
use your method as
change your method name to
-(void)setSpecialproperty:(MySpecialProperty *)specialproperty
-(void)setSpecialproperty:(MySpecialProperty*)specialproperty
{
if(_specialproperty!=specialproperty)
_specialproperty = specialproperty;
}
and synthesize your specialProperty as
#synthesize specialproperty=_specialproperty;