转:ios4与ios3兼容 -4 --Tips & Tricks for conditional iOS3, iOS3.2 and iOS4 code _iphone

转:ios4与ios3兼容 -4 --Tips & Tricks for conditional iOS3, iOS3.2 and iOS4 code _iphone

转: http://cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html

[2012.5.18:
使用 [[ UIDevice  currentDevice ]  systemVersion ]; 更为靠谱。参考:
http://stackoverflow.com/questions/3339722/check-iphone-ios-version/3339787#3339787

Apple uses systemVersion in their GLSprite sample code, so my recommendation can't be absolute:

// A system version of 3.1 or greater is required to use CADisplayLink. The NSTimer
// class is used as fallback when it isn't available.
NSString *reqSysVer = @"3.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
    displayLinkSupported
= TRUE;

Important Note: If for whatever reason you decide that systemVersion is what you want, make sure to treat it as an string or you risk truncating the minor revision number (eg. 3.1.2 -> 3.1).

不过这种方法对于小版本号码似乎不太好用。但是, 一般我们也不用细化到小版本号码吧。
]

[自:需要代理才能打开。]
[自:经过测试,目前整理如下:
*根据apple的声明,新的程序必须使用sdk 4编译。
*当使用sdk4编译程序时,同样可以将程序发布到ios3的机器中。方法就是文中提到的更改工程选项"Base SDK"和"iPhone OS Deployment Target",其中后者是指定程序可以运行的程序的最小ios值。如果这个值指定为ios3,则虽然在xcode的device中的内容是ios4,但是也是可以直接部署到ios3上的。
*使用#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000可以保证使用sdk4编译的程序一定可以执行到这里的代码,但是使用sdk3(需要使用旧版本的xcode才可以指定sdk3)的代码则不会执行到这里。但是,由于apple声明,必须使用ios4的代码,所以这个选项基本上是没有什么用处的,因为一定要使用ios4,所以一定会走这个宏内部定义的代码的。除非,你还要兼容旧版本的xcode。
*使用if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_4_0)可以动态的判断当前的ios是3还是4。这样可以根据情况使用不同sdk平台的代码。这个才是关键!!。
*作者给出的
IF_IOS4_OR_GREATER其实没有什么用。因为作为__IPHONE_OS_VERSION_MAX_ALLOWED的判断是多余的。
]

[自:补充:连接framework的时候,需要指定weak link的方式。具体方法,参照其他邮件。
简单提示:target-info-General-frameworks-type-weak]

Tips & Tricks for conditional iOS3, iOS3.2 and iOS4 code

In this post, I'll show you ways to determine which version of iOS you are running on and show you how to write a macro that can both conditionally compile and runtime switch between the code for different versions of iOS.

A project or target that supports multiple versions of iOS

To make an application target that runs on multiple versions of iOS is relatively simple:

  • Set the "Base SDK" in your projects settings to the newest version number of iOS whose features you may want.
  • Set the "iPhone OS Deployment Target" to the oldest version number of iOS that you will support

However, getting the target settings correct is the easy part of the problem. The hard part is using new features on newer iOS versions without breaking the app on older versions.

Running in the 3.1.3 simulator

Before getting to the actual code, it might be worthwhile to discuss how to run your projects in older versions of the simulator.

The simulator is an important part of iOS development (since it is much faster and simpler than running your code on the device). But Apple have removed SDK versions earlier than 3.2 from the current Xcode builds. This makes it hard to verify your apps on earlier devices unless you install on a physical device.

Support for 3.1.3 is relatively important since it is the last version of iOS supported by the original iPhone and iPod Touch and it will be a few months before iOS 4 exceeds 80% of the remaining iPhone and iPod Touch market.

To allow simulation in 3.1.3, you must install an old version of Xcode. If you are a registered iPhone developer, you can download Xcode 3.1.4 for Leopard with iPhone SDK 3.1.3 or Xcode 3.1.4 for Snow Leopard with iPhone SDK 3.1.3. Be careful to install these in a different location to your Xcode 3.2.3 with iOS3.2/iOS4 (either select a different hard disk or rename your existing /Developer directory before you install).

Once you've got an old version of Xcode, you'll want to duplicate your main target and set the Base SDK to 3.1.3 in this duplicate (because it won't exist in this version of Xcode). You should use a second target for this because you shouldn't risk messing with your main target just to run code in the simulator.

Using features from newer iOS versions while supporting older iOS versions

For example, if you want to start an iOS4 background task in an application that you want to run on earlier versions of iOS, then you'll need to use code like this:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
if ([[UIApplication sharedApplication]
respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)])
{
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{}];

// Perform work that should be allowed to continue in background

[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}
#endif

There are three important components:

  1. The #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 4000 compile-time conditional. This ensures that if we choose to build this project with a Base SDK lower than 4.0, then it won't cause compile problems. This is essential for running in older versions of the simulator.
  2. The runtime check that UIApplication supports the beginBackgroundTaskWithExpirationHandler method. Since the final release build will be built against the 4.0 SDK (even if users install on SDK 3.0) this runtime check ensures that the method we need is available.
  3. Everything else between the #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 4000 and the#endif is the iPhone OS 4 code.

Making the conditional work less ugly

The problem with the previous code is the compile-time conditional and the runtime check for the presence of methods is cumbersome since you must remember to to both.

If you want to integrate both a compile-time check and a runtime check, a better approach would look like this:

IF_IOS4_OR_GREATER
(
UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{}];

// Perform work that should be allowed to continue in background

[[UIApplication sharedApplication] endBackgroundTask:bgTask];
);

We can implement this macro as follows:

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_4_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_4_0 550.32
#endif

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
#define IF_IOS4_OR_GREATER(...) \
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
{ \
__VA_ARGS__ \
}
#else
#define IF_IOS4_OR_GREATER(...)
#endif

If we want to include something only in OS versions prior to a a specific version, then we don't need the conditional compilation (since we still want the code to appear when compiled in a later version. In this case, only a runtime check is required. You can either do this directly, or for symmetry with other macros, you could use:

#define IF_PRE_IOS4(...) \
if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
{ \
__VA_ARGS__ \
}

Three interesting points to note about these macros:

  1. I use the kCFCoreFoundationVersionNumber to determine the iPhone OS at runtime. There are many examples on the web using [[UIDevice currentDevice] systemVersion] but that method requires a string comparison and potentially handling of major and minor numbers within the string components. A singledouble comparison is far more straightforward.
  2. I have not used the typical do { x } while (0) wrapper around the macro, so you can simply tack an else onto the end if you choose (and it doesn't need conditional compilation of its own).
  3. I use a variable argument list for the macro. This is so that any number of commas may appear in the contents without causing problems.

A final point... the kCFCoreFoundationVersionNumber definitions may not be in every version of the SDK (each SDK normally contains definitions for versions up to but not including itself), so you should conditionally define them yourself in case they're missing. Here's a handy list:

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_0 478.23
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_1
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_1 478.26
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_2_2
#define kCFCoreFoundationVersionNumber_iPhoneOS_2_2 478.29
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_0 478.47
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_1
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_1 478.52
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_2
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_2 478.61
#endif

#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_4_0
#define kCFCoreFoundationVersionNumber_iPhoneOS_4_0 550.32
#endif

Better still: solutions that don't require macros

Better than a simple macro is a simple function. This is a valid solution where the contents of your conditional code does not itself contain OS specific code (only the condition itself requires OS specific logic).

A common example is handing separate layout for iPad and iPhone versions. Ordinarily, if you're compiling for iPad 3.2 and iPhone 3.1.3, you need the following code:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
if ([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] &&
[[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
// iPad specific layout changes
}
else
#endif
{
// iPhone layout
}

You can handle this with a conditional macro like the IF_IOS4_OR_GREATER but a far better solution is:

if (isIPad())
{
// iPad specific layout changes
}
else
{
// iPhone layout
}

Where all the conditional pollution is tidily kept in your isIPad() function:

BOOL isIPad()
{
IF_3_2_OR_GREATER
(
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
return YES;
}
);

return NO;
}

Conclusion

Apple doesn't exactly make it easy to support old versions of the iPhone SDK. I'm sure they want everyone to keep up to date or buy new devices if their current device can't be updated.

That's not always a realistic attitude for App Store developers. You can't expect all your customers to upgrade as soon as possible.

The important point when writing for multiple versions of the SDK is to keep as few conditionals as possible. You don't want to have thousands of conditionals in your code for supporting different versions. Every conditional is extra testing work since different behaviors must be fully exercised on all different platforms.

While the conditionals and functions I've talked about here will help, if you find yourself needing a lot of conditionals you may also want to consider design changes like instantiating different subclasses for different OS versions.



+++++

你可能感兴趣的:(转:ios4与ios3兼容 -4 --Tips & Tricks for conditional iOS3, iOS3.2 and iOS4 code _iphone)