如何通过编程确定我的应用程序是否在iPhone模拟器上运行?

295

正如问题所述,我主要想知道我的代码是否在模拟器中运行,并且也想知道正在运行或模拟的具体 iPhone 版本。

编辑:我在问题名称中添加了“以编程方式”,我的问题的重点是能够根据正在运行的版本/模拟器动态包含/排除代码,因此我真正需要的是类似于预处理器指令的东西,可以为我提供这些信息。


我不确定预处理器指令是否具有动态性(尽管它可能是您正在寻找的内容)。该指令意味着在构建时实际上已知它将在哪里运行。 - WiseOldDuck
据我所知,我的目标只是一个新的iPhone或模拟器,我喜欢__x86_64__(iPhone模拟器)和__arm64__(iPhone设备)。 - rustyMagnet
21个回答

366

已经有人问过这个问题,但标题不同。

在编译 iPhone 应用程序时 Xcode 设置了哪些 #define

我会重复我的答案:

在 SDK 文档的 "有条件地编译源代码" 章节中有相关定义。

相关定义是 TARGET_OS_SIMULATOR,在 iOS 框架内的 /usr/include/TargetConditionals.h 中定义。在早期版本的工具链中,你需要编写:

#include "TargetConditionals.h"

不过,在当前的(Xcode 6/iOS8)工具链上,这已经不再是必要的了。

因此,例如,如果您想检查是否在设备上运行,应该执行以下操作:

#if TARGET_OS_SIMULATOR
    // Simulator-specific code
#else
    // Device-specific code
#endif

根据您的用例选择适当的选项。


1
谢谢。我同意你的观点,这是你原来问题的更具体版本。如果你的问题在我的原始搜索中出现,我甚至都不需要问了。 - Jeffrey Meyer
5
请注意这些定义。当您使用菜单项“项目>设置活动SDK>模拟器…”编译代码时,TARGET_IPHONE_SIMULATOR和TARGET_OS_IPHONE变量都被定义了!因此,唯一正确的分离逻辑的方法如Pete所指出的那样(感谢你,伙计)。 - Vadim
5
观察#if和#ifdef的区别。对我来说,这是导致不正确行为的原因。 - Anton
7
也许自这段话写出来以来,包括TargetConditionals的需要已经不存在了。但是我只是想指出现在#if TARGET_IPHONE_SIMULATOR可以正常使用而不必包含TargetConditionals.h文件。 - dmur
1
@Dimitris 这是一个好的实践。你不知道 TARGET_OS_SIMULATOR 是如何定义的,因此 !(TARGET_OS_SIMULATOR) 可能与 !TARGET_OS_SIMULATOR 不完全相同。 - Airsource Ltd
显示剩余9条评论

106

更新的代码:

据称这是官方认可的有效代码。

#if TARGET_IPHONE_SIMULATOR
NSString *hello = @"Hello, iPhone simulator!";
#elif TARGET_OS_IPHONE
NSString *hello = @"Hello, device!";
#else
NSString *hello = @"Hello, unknown target!";
#endif

原始帖子 (已不建议使用)

这段代码可以告诉你是否在模拟器中运行。

#ifdef __i386__
NSLog(@"Running in the simulator");
#else
NSLog(@"Running on a device");
#endif

7
从 iOS 8 和 Xcode 6.1.1 开始,在模拟器上 TARGET_OS_IPHONE 是 true。 - malhal
4
这在较新的XCode版本上已经无法工作。 - Fabio Napodano
1
除非你在2016年并运行64位模拟器,或者在2019年并在搭载英特尔处理器的iPhone上运行代码。 - gnasher729

61

这不是预处理器指令,但当我看到这个问题时,这正是我在寻找的东西;

NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
    //device is simulator
}

9
[model compare:iPhoneSimulator] == NSOrderedSame 应该写成 [model isEqualToString:iPhoneSimulator] - user102008
18
如果你只关心模拟器是否为“模拟器”而不是特定于iPhone或iPad,可以使用[model hasSuffix:@"Simulator"]。但这个方法对于iPad模拟器不起作用 :) - Nuthatch
因为Nuthatch的评论使得这个答案成为了最佳答案,所以我点了赞。 - Le Mot Juiced
12
在iOS9中,请检查设备的“名称”而不是“型号”。 - n.Drake
2
如果用户在设备名称中添加了“模拟器”一词,则代码将无法正常工作。 - mbelsky

60

现在在Swift中有更好的方法。

Xcode 9.3及更高版本开始,您可以使用#if targetEnvironment(simulator)进行检查。

#if targetEnvironment(simulator)
//Your simulator code
#endif

2
这是唯一对我有效的方法,其他解决方案都没有起作用。 - Vrutin Rathod

55

最好的方法是:

#if TARGET_IPHONE_SIMULATOR

而不是

#ifdef TARGET_IPHONE_SIMULATOR

由于它总是定义的:0或1


45

对于Swift,我们可以按照以下方式实现:

我们可以创建一个结构体,允许您创建结构化数据。

struct Platform {
    static var isSimulator: Bool {
        #if targetEnvironment(simulator)
            // We're on the simulator
            return true
        #else
            // We're on a device
             return false
        #endif
    }
}

如果我们想要在Swift中检测应用程序是为设备还是模拟器构建的,则:

if Platform.isSimulator {
    // Do one thing
} else {
    // Do the other
}

在我看来,这是最干净的实现方式,并且考虑了x86_64和i386架构。帮助我克服了Core Data中奇怪的设备与模拟器错误。你太棒了! - Iron John Bonney
5
在 Playground 中,您将收到一个警告:“return 后面的代码将永远不会执行”。因此,我认为使用 #if #else #endif 会更好。 - DawnSong

32

适用于 Swift 4.1 及更高版本和 Xcode 9.3 及更高版本

使用此代码:

#if targetEnvironment(simulator)
   // Simulator
#else
   // Device
#endif

这基本上是在4个月前提供的解决方案的重复。 - Cristik

9
所有这些答案都很好,但它有点让像我这样的新手困惑,因为它没有澄清编译检查和运行时检查。预处理器在编译时之前,但我们应该使其更清晰。
这篇博客文章清楚地展示了如何检测 iPhone 模拟器。
运行时
首先,让我们简要讨论一下。UIDevice 已经为您提供了有关设备的信息。
[[UIDevice currentDevice] model]

该代码将根据应用程序运行的位置返回“iPhone Simulator”或“iPhone”。

编译时

然而,您想要使用编译时定义。为什么?因为您严格编译应用程序以在模拟器内部或设备上运行。苹果公司创建了一个名为TARGET_IPHONE_SIMULATOR的定义。因此,让我们看一下代码:

#if TARGET_IPHONE_SIMULATOR

NSLog(@"Running in Simulator - no app store or giro");

#endif

1
这种方法有何改进之处? - mmmmmm
@Mark 它澄清了一点点。 - onmyway133
6
目前,在 Xcode 7 和 iOS 9 模拟器中,[[UIDevice currentDevice] model] 返回的是“iPhone”,而不是“iPhone Simulator”。因此,我认为这不是最好的方法。 - eMdOS
有没有办法获取“iPhone 14”等等? - Unome

8

针对Swift 4.2 / Xcode 10

我对UIDevice创建了一个扩展,这样我就可以轻松地检查模拟器是否在运行。

// UIDevice+CheckSimulator.swift

import UIKit

extension UIDevice {
    
    /// Checks if the current device that runs the app is xCode's simulator
    static func isSimulator() -> Bool {        
        #if targetEnvironment(simulator)
            return true
        #else
            return false
        #endif
    }
}

比如在我的AppDelegate中,我使用这个方法来决定是否需要注册远程通知,但模拟器不支持该功能。

// CHECK FOR REAL DEVICE / OR SIMULATOR
if UIDevice.isSimulator() == false {
        
    // REGISTER FOR SILENT REMOTE NOTIFICATION
    application.registerForRemoteNotifications()
}

6
之前的回答有点过时了。我发现你只需要查询 TARGET_IPHONE_SIMULATOR 宏(假设你正在编写iOS应用,不需要包含任何其他头文件)即可。
我尝试了 TARGET_OS_IPHONE,但在实际设备和模拟器上运行时返回相同的值(1),这就是为什么我建议使用 TARGET_IPHONE_SIMULATOR 的原因。

TARGET_OS_IPHONE 是用于可能在 iOS 或 MacOS X 上运行的代码。显然,您希望该代码在模拟器上表现出“iPhone”的方式。 - gnasher729

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接