Как говорится в вопросе, я в основном хотел бы знать, работает ли мой код в симуляторе, но мне также было бы интересно узнать конкретную версию iphone, которая работает или моделируется.
Обновлено: я добавил слово «программно» к названию вопроса. Суть моего вопроса в том, чтобы иметь возможность динамически включать / исключать код в зависимости от того, какая версия / симулятор работает, поэтому я действительно буду искать что-то вроде директивы препроцессора, которая может предоставить мне эту информацию.
Поскольку я знаю, что моя цель - только новый iPhone или симулятор, мне нравятся __x86_64__ (симулятор iPhone) и __arm64__ (устройство iPhone).





Предполагается, что это работает официально.
#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
Начиная с iOS 8 и Xcode 6.1.1 TARGET_OS_IPHONE истинен на симуляторе.
это больше не волнует в новых версиях XCode
Если только вы не находитесь в 2016 году и не запускаете 64-битный симулятор. Или в 2019 году и запустите свой код на iPhone с процессором Intel.
Уже спрашивали, но с совсем другим названием.
Какие #defines устанавливает Xcode при компиляции для iPhone
Я повторю свой ответ оттуда:
Это находится в документации SDK в разделе «Условная компиляция исходного кода».
Соответствующее определение - TARGET_OS_SIMULATOR, которое определено в /usr/include/TargetConditionals.h в рамках iOS. В более ранних версиях цепочки инструментов вам нужно было написать:
#include "TargetConditionals.h"
но в текущей (Xcode 6 / iOS8) цепочке инструментов в этом больше нет необходимости.
Так, например, если вы хотите проверить, что вы работаете на устройстве, вам следует сделать
#if TARGET_OS_SIMULATOR
// Simulator-specific code
#else
// Device-specific code
#endif
в зависимости от того, что подходит для вашего варианта использования.
Спасибо. Я согласен с вами, это более конкретная версия вашего исходного вопроса. Если бы ваш вопрос появился в моем первоначальном поиске, мне бы даже не пришлось спрашивать.
Да, я понимаю, что это нелегко найти в поиске - я просто ссылался на него как на дубликат, чтобы люди могли читать комментарии в обоих местах.
Будьте осторожны с этими определениями. Когда вы компилируете код с помощью пункта меню «Проект> Установить активный SDK> Симулятор…», как TARGET_IPHONE_SIMULATOR, так и переменные TARGET_OS_IPHONE определяются! Итак, единственный правильный способ разделения логики указан ниже Питом (спасибо, чувак).
Верно, но когда вы компилируете для устройства, TARGET_IPHONE_SIMULATOR не устанавливается, так почему бы просто не включить это? Включение i386 зависит от определений инструментальной цепочки и будет недействительным при компиляции на платформе, отличной от x86 (в настоящее время это, конечно, невозможно).
Обратите внимание на разницу #if и #ifdef. Для меня это было причиной некорректного поведения.
как сказано здесь: stackoverflow.com/questions/3742525/… это неверно для статических библиотек
Возможно, необходимость включения TargetConditionals отпала с тех пор, как это было написано, но просто хотел отметить, что #if TARGET_IPHONE_SIMULATOR теперь работает без включения TargetConditionals.h.
Не лучше ли использовать обычный if вместо препроцессора #if, чтобы вы могли получить ошибки компилятора в обоих путях управления? Препроцессор #if не компилирует ветвь, на которую вы в настоящее время не нацеливаетесь. Вы не хотели бы иметь ложное чувство выполненного долга, когда весь ваш код работает на симуляторе, но ваша реальная ветвь устройства сломана.
Вы можете использовать # if-код, который на самом деле не компилируется на той или иной платформе, но да, вы могли бы использовать if. Не знаете, как это помогает с ложным чувством достижения, вам все равно нужно протестировать его на обеих платформах!
Почему скобки () вокруг TARGET_OS_SIMULATOR?
Мне нужно было включить в файл Obj-C для iOS 9 с использованием Xcode 7. Хм.
@Oren см. stackoverflow.com/questions/24869481/…
@Dimitris Это хорошая практика. Вы не знаете, как был определен TARGET_OS_SIMULATOR, поэтому! (TARGET_OS_SIMULATOR) может не совпадать с! TARGET_OS_SIMULATOR
Сейчас есть лучший способ сделать это. Смотрите мой ответ в этой теме stackoverflow.com/a/49236898/2272561
У меня была та же проблема, и TARGET_IPHONE_SIMULATOR, и TARGET_OS_IPHONE всегда определены и имеют значение 1. Решение Пита, конечно, работает, но если вы когда-нибудь будете использовать что-то другое, кроме Intel (маловероятно, но кто знает), вот что-то, что безопасно, пока оборудование iphone не меняется (так что ваш код всегда будет работать для имеющихся в настоящее время iphone):
#if defined __arm__ || defined __thumb__
#undef TARGET_IPHONE_SIMULATOR
#define TARGET_OS_IPHONE
#else
#define TARGET_IPHONE_SIMULATOR 1
#undef TARGET_OS_IPHONE
#endif
Поместите это в удобное место, а затем сделайте вид, что константы TARGET_* определены правильно.
Не директива препроцессора, но это было то, что я искал, когда подошел к этому вопросу;
NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
//device is simulator
}
[model compare:iPhoneSimulator] == NSOrderedSame должен быть записан как [model isEqualToString:iPhoneSimulator]Или [model hasSuffix:@"Simulator"], если вас интересует только «симулятор» в целом, а не iPhone или iPad в частности. Этот ответ не сработает для симулятора iPad :)
Проголосовали за, потому что комментарий Поползня делает это лучшим ответом в целом.
В iOS9 проверьте устройство name вместо model
Код не будет работать, если пользователь добавит слово Simulator в имя своего устройства.
Лучший способ сделать это:
#if TARGET_IPHONE_SIMULATOR
и нет
#ifdef TARGET_IPHONE_SIMULATOR
поскольку он всегда определен: 0 или 1
Предыдущие ответы немного устарели. Я обнаружил, что все, что вам нужно сделать, это запросить макрос TARGET_IPHONE_SIMULATOR (нет необходимости включать какие-либо другие файлы заголовков [при условии, что вы кодируете для iOS]).
Я попытался использовать TARGET_OS_IPHONE, но он вернул то же значение (1) при работе на реальном устройстве и симуляторе, поэтому я рекомендую вместо этого использовать TARGET_IPHONE_SIMULATOR.
TARGET_OS_IPHONE предназначен для кода, который может работать на iOS или MacOS X. Очевидно, вы хотите, чтобы этот код вел себя как «iPhone» на симуляторе.
На мой взгляд, ответ (представленный выше и повторенный ниже):
NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:@"iPhone Simulator"]) {
//device is simulator
}
- лучший ответ, потому что он, очевидно, выполняется во время RUNTIME, а не является КОМПИЛЬНОЙ ДИРЕКТИВОЙ.
Я не согласен. Этот код попадает в ваш продукт, тогда как директива компилятора избавляет устройство от ненужной рутины.
Директивы компилятора работают, потому что устройство и симуляторы являются совершенно разными целями компиляции, то есть вы не можете использовать один и тот же двоичный файл на обоих. имеет должен быть скомпилирован для другого оборудования, поэтому в этом случае это имеет смысл.
Выполнение в RUNTIME делает его возможным ответом худший.
Все эти ответы хороши, но это как-то сбивает с толку новичков, таких как я, поскольку не разъясняет проверку компиляции и проверку времени выполнения. Препроцессор находится до компиляции, но мы должны сделать его более понятным
Эта статья блога ясно показывает Как определить симулятор iPhone?
Время выполнения
Прежде всего, давайте вкратце обсудим. UIDevice уже предоставляет вам информацию об устройстве
[[UIDevice currentDevice] model]
вернет вам «iPhone Simulator» или «iPhone» в зависимости от того, где запущено приложение.
Время компиляции
Однако вы хотите использовать определения времени компиляции. Почему? Потому что вы компилируете свое приложение строго для запуска либо внутри симулятора, либо на устройстве. Apple делает определение под названием TARGET_IPHONE_SIMULATOR. Итак, давайте посмотрим на код:
#if TARGET_IPHONE_SIMULATOR
NSLog(@"Running in Simulator - no app store or giro");
#endif
Как это лучше других ответов?
@Mark Немного проясняет
В настоящее время в Xcode 7 симулятор iOS 9 [[UIDevice currentDevice] model] также возвращает iPhone вместо iPhone Simulator. Так что я думаю, что это не лучший подход.
Включить все виды «тренажеров»
NSString *model = [[UIDevice currentDevice] model];
if ([model rangeOfString:@"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
// we are running in a simulator
}
Это не имеет ничего общего с Xcode 7. Если вы запустите iOS Simulator с iOS8 (из Xcode 7), то это сработает. Это не будет работать для iOS9, где [[UIDevice currentDevice] model] возвращает только «iPhone», если приложение было запущено из iOS Simulator.
почему не -[NSString containsString]?
Мой ответ основан на ответе @Daniel Magnusson и комментариях @Nuthatch и @ n.Drake. и я пишу его, чтобы сэкономить время для быстрых пользователей, работающих на iOS9 и новее.
Вот что сработало для меня:
if UIDevice.currentDevice().name.hasSuffix("Simulator"){
//Code executing on Simulator
} else{
//Code executing on Device
}
Код не будет работать, если пользователь добавит слово Simulator в имя своего устройства.
К сожалению, с XCode 8 UIDevice.current.name сообщает имя машины, на которой работает симулятор (обычно что-то вроде «Simon's MacBook Pro» сейчас), поэтому тест стал ненадежным. Я все еще ищу чистый способ исправить это.
В случае 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. Ты мужчина!
В Playground вы получите предупреждение: «Код после возврата никогда не будет выполнен». Так что думаю #if #else #endif будет лучше.
Это сработало для меня лучше всего
NSString *name = [[UIDevice currentDevice] name];
if ([name isEqualToString:@"iPhone Simulator"]) {
}
В Xcode 7.3 симулятор iPhone 6 Plus возвращает "iPhone".
Быстро:
#if (arch(i386) || arch(x86_64))
...
#endif
От Определите, создается ли приложение для устройства или симулятора в Swift
Чтобы различать приложения для Mac: #if (arch (i386) || arch (x86_64)) &&! Os (OSX) // мы находимся на симуляторе, работающем на Mac, а не в приложении для Mac. (Для кросс-платформенного кода, включенного в цели Mac)
Кто-нибудь считал предоставленный ответ здесь?
Я полагаю, что эквивалент objective-c будет
+ (BOOL)isSimulator {
NSOperatingSystemVersion ios9 = {9, 0, 0};
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
if ([processInfo isOperatingSystemAtLeastVersion:ios9]) {
NSDictionary<NSString *, NSString *> *environment = [processInfo environment];
NSString *simulator = [environment objectForKey:@"SIMULATOR_DEVICE_NAME"];
return simulator != nil;
} else {
UIDevice *currentDevice = [UIDevice currentDevice];
return ([currentDevice.model rangeOfString:@"Simulator"].location != NSNotFound);
}
}
/// Возвращает истину, если это симулятор, а не устройство
public static var isSimulator: Bool {
#if (arch(i386) || arch(x86_64)) && os(iOS)
return true
#else
return false
#endif
}
ЕСТЬ ЛУЧШИЙ СПОСОБ!
Начиная с Xcode 9.3 beta 4, вы можете использовать #if targetEnvironment(simulator) для проверки.
#if targetEnvironment(simulator)
//Your simulator code
#endif
ОБНОВИТЬ
Xcode 10 и iOS 12 SDK тоже поддерживают это.
Это единственное, что у меня работает, остальные решения не работали.
Примечание. Это только в быстром темпе.
С Swift 4.2 (Xcode 10) мы можем это сделать
#if targetEnvironment(simulator)
//simulator code
#else
#warning("Not compiling for simulator")
#endif
Просто еще одна копия вставки
Apple добавила поддержку проверки того, что приложение предназначено для симулятора, со следующим:
#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif
Работает для Swift 5 и Xcode 12
Используйте этот код:
#if targetEnvironment(simulator)
// Simulator
#else
// Device
#endif
если ничего не сработало, попробуйте это
public struct Platform {
public static var isSimulator: Bool {
return TARGET_OS_SIMULATOR != 0 // Use this line in Xcode 7 or newer
}
}
Для 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()
}
Я не уверен, что директива препроцессора является динамической (хотя в любом случае это может быть то, что вы искали). Директива означает, что вы действительно знали, когда создавали его, где он будет работать.