#interface Demo2ViewController () <GMGridViewDataSource, GMGridViewSortingDelegate, GMGridViewTransformationDelegate>
{
__gm_weak GMGridView *_gmGridView1;
__gm_weak GMGridView *_gmGridView2;
__gm_weak UIButton *_buttonOptionsGrid1;
__gm_weak UIButton *_buttonOptionsGrid2;
UIPopoverController *_popOverController;
UIViewController *_optionsController1;
UIViewController *_optionsController2;
}
Trying out the GMGridview, and saw this __gm_weak GMGridView *_gmGridView1;. Anyone know what __gm_weak means and when do you use __? Thanks in advance.
those declarations are not common, they are defined in the GMGridView sources:
//
// ARC on iOS 4 and 5
//
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_5_0 && !defined (GM_DONT_USE_ARC_WEAK_FEATURE)
#define __gm_weak __weak
#else
#define __gm_weak __unsafe_unretained
#endif
as far as the double underscore usage, i'd advise against it. just consider it reserved for the implementation (e.g. toolchain) and use a normal identifier.
Related
I have a scenario where I am trying to create a generic C++ class (across platforms) that can operate on an Array . This C++ class is meant to be portable across OS's such that it can leverage the native Array support.
For example, it'll use NSArray when running on Mac OS / iOS, or an MFC array class in Windows. My goal is to make this array management class generic.
#ifndef __MixTest__ArrayUser__
#define __MixTest__ArrayUser__
#if defined _MAC_OS
typedef void * ARRAY_HANDLE;
//#elif defined _WINDOWS
#endif
class ArrayUser
{
public:
ArrayUser() { }
virtual ~ArrayUser() { }
void assignArray(ARRAY_HANDLE handle)
{
m_array_handle = handle;
}
void UseArray();
private:
ARRAY_HANDLE m_array_handle;
};
#endif /* defined(__MixTest__ArrayUser__) */
// ArrayUser.cpp
// MixTest
#include "ArrayUser.h"
#import <Cocoa/Cocoa.h>
void ArrayUser::UseArray()
{
NSArray *array = (__bridge NSArray *) m_array_handle;
for (NSString *str in array)
{
NSLog(#"Array - %#",str);
}
}
Now my goal is to run this on Mac OS and I do the following in my barebones Mac OS project
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (strong) NSArray *arrayItems;
#end
Implementation
#import "AppDelegate.h"
#include "ArrayUser.h"
#interface AppDelegate ()
{
ArrayUser arrayUser;
}
#end
#implementation AppDelegate
-(id) init
{
if([super init] !=nil)
{
_arrayItems = #[#"Planes",#"Trains",#"Automobiles"];
arrayUser.assignArray((__bridge ARRAY_HANDLE)_arrayItems);
arrayUser.UseArray();
}
return self;
}
Questions
Is this transfer appropriate ?
arrayUser.assignArray((__bridge ARRAY_HANDLE)_arrayItems);
The expectation is that _arrayItems being strong and is expected to be controlled via the AppDelegate.
If _arrayItems weren't defined as being strong, then what is the
right way to bridge such that the object cannot be destroyed
(because ArrayUser needs it ) ?
Should the bridging be the same irrespective of whether or not
_arrayItems is strong or weak? The fact that ArrayUser needs it, should indicate to ARC that the object cannot be deallocated. What
is the bridging in this case ?
Trying to write some common libraries that work between iOS and OS X. In order to not clutter up the apps by putting tons of #if TARGET_OS_IPHONE in the code I figured I would be able to subclass both UIFont and NSFont using a common .h and just use separate .m files for each build. So I have the following subclass structure MyFont.h and MyFont_iOS.m and MyFont_OSX.m. Each target includes the appropriate .m file. Works fine on iOS but when executing on OSX I get an EXC_BAD_ACCCES creating the font object with a line like
MyFont *myFont = [MyFont fontWithName:#"Arial" size:132.0];
Can I not subclass NSFont?
Here's my basic code sample for the NSFont subclass (note as of this time I have no specialized code in the subclass. This is just a basic subclass)
MyFont.h
#if TARGET_OS_IPHONE
#interface MyFont : UIFont
#else
#interface MyFont : NSFont
#endif
#end
MyFont_iOS.m
#import "MyFont.h"
#implementation MyFont
#end
MyFont_OSX.m
#import "MyFont.h"
#implementation MyFont
#end
Attempted Use of the sub classed font (in iOS App - Works Fine!)
#import "MyFont.h"
...
MyFont *myFont = [MyFont fontWithName:#"Arial" size:45.0];
Attempted Use of the sub classed font (in OS X App - Crashes!)
#import "MyFont.h"
...
MyFont *myFont = [MyFont fontWithName:#"Arial" size:132.0];
FYI - I'm also getting the warning on this line
incompatible pointer types initializing 'MyFont *' with an expression of type 'NSFont *'
So I'm definitely doing something wrong but just don't understand why it's forcing me to use NSFont but works fine on UIFont.
Thanks for any guidance!
fontWithName returns a NSFont/UIFont - fixed. IT cant be subclassed
Even if it works, it is not defined behaviour
what you can do is have a wrapper that HOLDS the platform specific font
e.g.
#implementation MyFont {
PlatformSpecificFont *_font
}
+ (instancetype)fontWithName:... {
_font = [PlatformSpecificFont fontWithName:...];
return self;
}
Wrapper is a good way to do this. An alternative: Depending on the target, either
#define MyFont NSFont
or
#define MyFont UIFont
And where both require different code, you can add extensions to the class.
How can I mark a #property in Objective C as deprecated?
Unless you really want to deprecate based on iOS version, which I suspect you don't want to do, you can use DEPRECATED_ATTRIBUTE
#property (strong) NSObject *object DEPRECATED_ATTRIBUTE;
from NSObjCRuntime.h
#define NS_AVAILABLE(_mac, _ios)
#define NS_AVAILABLE_MAC(_mac)
#define NS_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep)
#define NS_DEPRECATED_MAC(_macIntro, _macDep)
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep)
you can use these macros
this is one example in UITableViewCell.h
#property(nonatomic,copy) NSString *text NS_DEPRECATED_IOS(2_0, 3_0); // default is nil
You can also use DEPRECATED_MSG_ATTRIBUTE("Use anotherProperty instead.") and provide a meaningful message hinting the user what to use in place of the deprecated property.
Xcode will show a warning when the property is used.
example:
#property (nonatomic) NSString *someProperty DEPRECATED_MSG_ATTRIBUTE("Use anotherProperty instead.");
you can type macro like this below one in .pch to be gloabl for all the app
#define DEPRECATED_ATTRIBUTE __attribute__((deprecated))
in mark the method as following example
-(void) exmapleMethodName DEPRECATED_ATTRIBUTE{
// code
}
I have an Xcode 4 project that builds to two different targets. I've defined some constants in the build settings so I can run different code for each target like this:
#ifdef VERSION1
// do this
#else
// do that
#endif
In one version of the app, I need the main view controller to open another view controller and become its delegate, but the other version doesn't use that view controller and shouldn't compile its code or try to become its delegate. I've set up the main view controller header like this:
#ifdef VERSION2
#import "SpecialViewController.h"
#endif
#interface MainViewController : UIViewController <MPMediaPickerControllerDelegate, SpecialViewControllerDelegate> {
// etc.
The conditional around the #import tag works fine, but how can I declare this class to be the SpecialViewControllerDelegate in one version but not the other?
Just use a #define preprocessor directive to change the delegates between versions.
Here's an example for "VERSION2".
#ifdef VERSION2
#import "SpecialViewController.h"
#define ARGS PMediaPickerControllerDelegate, SpecialViewControllerDelegate
#endif
#interface MainViewController : UIViewController <ARGS>
As long as you don't assign the delegate you should be fine leaving the implementation. Your SpecialViewController in VERSION1 (if you even have a SpecialViewController in V1) will not have a delegate so its calls will go nowhere, which should lead to no side effects.
#ifdef VERSION2
specialViewController.delegate = self;
#endif
If this approach doesn't work it almost seems like you should have a different MainViewController for each target.
I am just learning how to use ScriptingBridges. I made a method that slowly fades the volume on iTunes, and would like to make it a category so I can do the following:
iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:50 speed:1];
I made another category for NSSpeechSynthesizer that works, but I can't get this one to. I keep getting the following build error:
"_OBJC_CLASS_$_iTunesApplication", referenced from:
l_OBJC_$_CATEGORY_iTunesApplication_$_iTunesApplicationAdditions in iTunesApplication.o
objc-class-ref-to-iTunesApplication in iTunesApplication.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Is there something special I can do to make it work since I can't include the symbols?
Thanks,
Ryan Pendleton
UPDATE:
I only found one solution, which is below. It involves MethodSwizzling, so I'm open to better answers, but for now it's all I have.
The solution I found was to use the Objective-C runtime API. I'm sure there's a better way to organize this, but here's how I did it:
Here are my .h and .m files for creating the category. Notice how lowerVolume is not an actual method, but a C function with the arguments id self, and SEL _CMD. You'll also notice a setupCategories function. We'll call that later.
// iTunes+Volume.h
#import <objc/runtime.h>
#import "iTunes.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed);
void setupCategories();
#interface iTunesApplication (Volume)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// iTunes+Volume.m
#import "iTunes+Volume.h"
void lowerVolume(id self, SEL _cmd, int dest, float speed)
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
void setupCategories()
{
id object = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
Class class = [object class];
[object release];
class_addMethod(class, #selector(lowerVolume:speed:), (IMP)lowerVolume, "#:if");
}
Now that I've made the functions, I need to actually add them to the scripting bridge class using the Objective-C runtime API. I'll do this in main.m to make sure that the methods are ready to be used when the run loop starts.
// main.m
#import <Cocoa/Cocoa.h>
#import "iTunes+Volume.h"
int main(int argc, char *argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
setupCategories();
return NSApplicationMain(argc, (const char **) argv);
[pool drain];
}
Now, I can use my method wherever I want as long as I include the header files:
- (void)mute
{
iTunesApplication* iTunes = [[SBApplication alloc] initWithBundleIdentifier:#"com.apple.iTunes"];
[iTunes lowerVolume:0 speed:1];
[iTunes release];
}
If any of this doesn't make sense, just tell me and I'll try to explain it better.
I think you need to include -framework ScriptingBridge to your gcc arguments. That got it to compile for me!
As noted above, you can't easily do a category on iTunesApplication because it doesn't exist at compile time, and also because the runtime class name is ITunesApplication (capital "I").
The best solution I've found is to do your category on the class that DOES exist, SBApplication. Here's the code I tested that works and does what the original example was trying to do:
// SBApplication+Extensions.h
#import ScriptingBridge;
#interface SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed;
#end
// SBApplication+Extensions.m
#import "iTunes.h"
#import "SBApplication+Extensions.h"
#implementation SBApplication (Extensions)
- (void)lowerVolume:(int)dest speed:(float)speed
{
NSLog(#"Lower Volume: %i, %f", dest, speed);
}
#end
// Caller, say in AppDelegate
#import "SBApplication+Extensions.h"
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
iTunesApplication *iTunesApp =
[SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
[iTunesApp lowerVolume:4 speed:3.3f];
}