ฉันจะทราบได้อย่างไรว่าโปรแกรมของฉันทำงานใน iphone simulator หรือไม่?


270

ตามที่คำถามระบุไว้ฉันต้องการทราบว่ารหัสของฉันทำงานในโปรแกรมจำลองเป็นหลักหรือไม่ แต่ก็สนใจที่จะทราบรุ่นของ iPhone ที่ใช้หรือกำลังจำลองอยู่

แก้ไข: ฉันเพิ่มคำว่า 'โดยทางโปรแกรม' ในชื่อคำถาม จุดประสงค์ของคำถามของฉันคือการสามารถรวม / แยกรหัสแบบไดนามิกขึ้นอยู่กับรุ่น / ตัวจำลองการทำงานดังนั้นฉันจะมองหาบางสิ่งบางอย่างเช่นคำสั่งประมวลผลล่วงหน้าที่สามารถให้ข้อมูลนี้แก่ฉันได้


ฉันไม่แน่ใจว่าคำสั่งของโปรเซสเซอร์ล่วงหน้านั้นเป็นแบบไดนามิก (แม้ว่าอาจเป็นสิ่งที่คุณกำลังมองหาอยู่แล้ว) คำสั่งหมายความว่าคุณรู้จริงเมื่อคุณสร้างมันที่มันจะจบลงด้วยการวิ่ง
WiseOldDuck

คำตอบ:


356

ถามแล้ว แต่มีชื่อแตกต่างกันมาก

#defines ถูกตั้งค่าโดย Xcode เมื่อรวบรวมสำหรับ iPhone

ฉันจะทำซ้ำคำตอบของฉันจากที่นั่น:

อยู่ในเอกสาร SDK ภายใต้ "การคอมไพล์ซอร์สโค้ดอย่างมีเงื่อนไข"

นิยามที่เกี่ยวข้องคือ TARGET_OS_SIMULATOR ซึ่งกำหนดไว้ใน /usr/include/TargetConditionals.h ภายในเฟรมเวิร์ก iOS ใน toolchain เวอร์ชันก่อนหน้าคุณต้องเขียน:

#include "TargetConditionals.h"

แต่สิ่งนี้ไม่จำเป็นใน toolchain ปัจจุบัน (Xcode 6 / iOS8) อีกต่อไป

ตัวอย่างเช่นหากคุณต้องการตรวจสอบว่าคุณใช้อุปกรณ์อยู่คุณควรทำ

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

ขึ้นอยู่กับว่าเหมาะสมกับการใช้งานของคุณหรือไม่


1
ขอบคุณ ฉันเห็นด้วยกับคุณนี่เป็นคำถามดั้งเดิมของคุณที่เฉพาะเจาะจงมากขึ้น หากคุณเกิดขึ้นในการค้นหาเดิมของฉันฉันไม่จำเป็นต้องถาม
เจฟฟรีย์เมเยอร์

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

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 นั้นเป็นจริงในตัวจำลอง
58

3
สิ่งนี้ไม่ต้องกังวลอีกต่อไปสำหรับรุ่น XCode ที่ใหม่กว่า
Fabio Napodano

1
ยกเว้นว่าคุณอยู่ในปี 2559 และใช้ตัวจำลอง 64 บิต หรือในปี 2019 และเรียกใช้รหัสของคุณบน iPhone ด้วยโปรเซสเซอร์ Intel
gnasher729

61

ไม่ใช่คำสั่ง pre-processor แต่นี่คือสิ่งที่ฉันกำลังมองหาเมื่อฉันมาถึงคำถามนี้

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

9
[model compare:iPhoneSimulator] == NSOrderedSameควรเขียนเป็น[model isEqualToString:iPhoneSimulator]
user102008

18
หรือ[model hasSuffix:@"Simulator"]ถ้าคุณจะดูแลเกี่ยวกับ "จำลอง" โดยทั่วไปไม่iPhoneหรือiPadโดยเฉพาะอย่างยิ่ง คำตอบนี้จะไม่ทำงานกับเครื่องจำลอง iPad :)
Nuthatch

เพิ่มขึ้นเนื่องจากความคิดเห็นของ Nuthatch ทำให้นี่เป็นคำตอบที่ดีที่สุดในโตโต้
Le Mot Juiced

12
ใน iOS9 ให้ตรวจสอบอุปกรณ์nameแทนmodel
n.Drake

1
รหัสจะไม่ทำงานหากผู้ใช้เพิ่มSimulatorคำในชื่ออุปกรณ์ของเขา
mbelsky

55

วิธีที่ดีที่สุดในการทำเช่นนี้คือ:

#if TARGET_IPHONE_SIMULATOR

และไม่

#ifdef TARGET_IPHONE_SIMULATOR

เนื่องจากมันถูกกำหนดเสมอ: 0 หรือ 1


39

มีวิธีที่ดีกว่าตอนนี้!

ในฐานะของ Xcode 9.3 เบต้า 4 คุณสามารถใช้#if targetEnvironment(simulator)เพื่อตรวจสอบ

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

อัปเดต
Xcode 10 และ iOS 12 SDK รองรับสิ่งนี้เช่นกัน


1
นี่เป็นสิ่งเดียวที่ใช้ได้กับฉันส่วนที่เหลือของโซลูชันไม่ทำงาน
Vrutin Rathod

หมายเหตุนี่เป็นเพียงรวดเร็ว
Matt S.

35

ในกรณีของ 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
ในสนามเด็กเล่นคุณจะได้รับคำเตือน "รหัสหลังจาก" ส่งคืน "จะไม่ถูกดำเนินการ" ดังนั้นฉันคิดว่า#if #else #endifจะดีกว่า
DawnSong


9

คำตอบทั้งหมดนั้นเป็นสิ่งที่ดี แต่ก็ทำให้เกิดความสับสนอย่างมือใหม่เช่นฉันเพราะมันไม่ได้ชี้แจงการตรวจสอบคอมไพล์และการตรวจสอบรันไทม์ ตัวประมวลผลล่วงหน้ามาก่อนเวลารวบรวม แต่เราควรทำให้ชัดเจนขึ้น

บทความบล็อกนี้แสดงวิธีการตรวจสอบเครื่องจำลอง iPhone อย่างเห็นได้ชัด

Runtime

ก่อนอื่นมาคุยกันเร็ว ๆ นี้ UIDevice ให้ข้อมูลเกี่ยวกับอุปกรณ์แก่คุณอยู่แล้ว

[[UIDevice currentDevice] model]

จะส่งคืน“ iPhone Simulator” หรือ“ iPhone” ตามที่แอพกำลังทำงาน

รวบรวมเวลา

อย่างไรก็ตามสิ่งที่คุณต้องการคือการใช้เวลารวบรวมกำหนด ทำไม? เนื่องจากคุณรวบรวมแอปของคุณอย่างเคร่งครัดเพื่อให้ทำงานได้ทั้งภายใน Simulator และบนอุปกรณ์ TARGET_IPHONE_SIMULATORแอปเปิ้ลทำให้กำหนดเรียกว่า ดังนั้นลองดูรหัส:

#if TARGET_IPHONE_SIMULATOR

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

#endif

1
สิ่งนี้ปรับปรุงจากคำตอบอื่น ๆ ได้อย่างไร?
user151019

@ Mark มันชี้แจงเล็กน้อย
onmyway133

5
ปัจจุบันใน Xcode 7, iOS ของคุณ 9 จำลอง[[UIDevice currentDevice] model]จะกลับยังแทนiPhone iPhone Simulatorดังนั้นฉันคิดว่านี่ไม่ใช่วิธีที่ดีที่สุด
eMdOS

6

คำตอบก่อนหน้านี้เป็นวันที่เล็กน้อย ฉันพบว่าสิ่งที่คุณต้องทำคือสืบค้นTARGET_IPHONE_SIMULATORแมโคร ( ไม่จำเป็นต้องรวมไฟล์ส่วนหัวอื่น ๆ [สมมติว่าคุณกำลังเข้ารหัสสำหรับ iOS])

ฉันพยายามTARGET_OS_IPHONEแต่มันคืนค่าเดียวกัน (1) เมื่อทำงานบนอุปกรณ์และตัวจำลองที่แท้จริงนั่นคือสาเหตุที่ฉันแนะนำให้ใช้TARGET_IPHONE_SIMULATORแทน


TARGET_OS_IPHONE สำหรับรหัสที่อาจทำงานบน iOS หรือ MacOS X เห็นได้ชัดว่าคุณต้องการให้รหัสนั้นทำงานแบบ "iPhone" ในเครื่องจำลอง
gnasher729

6

ในการแยกแยะความแตกต่างระหว่างแอป mac: #if (arch (i386) || arch (x86_64)) &&! os (OSX) // เราอยู่ในโปรแกรมจำลองการทำงานบน mac ไม่ใช่แอป mac (สำหรับรหัสข้ามแพลตฟอร์มที่รวมอยู่ในเป้าหมายของแม็ค)
Bobjt

4

ฉันมีปัญหาเดียวกันทั้งสองTARGET_IPHONE_SIMULATORและTARGET_OS_IPHONEถูกกำหนดไว้เสมอและตั้งค่าเป็น 1 แน่นอนว่าโซลูชันของ Pete ทำงานได้ แต่ถ้าคุณเคยสร้างสิ่งอื่นที่ไม่ใช่ Intel (ไม่น่าเป็นไปได้ แต่ใครจะรู้) นี่เป็นสิ่งที่ปลอดภัย ตราบใดที่ฮาร์ดแวร์ 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_*ค่าคงที่ถูกกำหนดไว้อย่างถูกต้อง


4

มีใครพิจารณาคำตอบที่มีให้ ที่นี่ ?

ฉันคิดว่าเป้าหมายเทียบเท่า -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);
    }
}

4

สำหรับ 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()
}

1

เพื่อรวม "เครื่องมือจำลอง" ทุกประเภท

NSString *model = [[UIDevice currentDevice] model];
if([model rangeOfString:@"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
    // we are running in a simulator
}

4
มันไม่มีส่วนเกี่ยวข้องกับ Xcode 7 หากคุณเรียกใช้ iOS Simulator กับ iOS8 (จาก Xcode 7) สิ่งนี้จะใช้งานได้ มันจะไม่ทำงานสำหรับ iOS9 ซึ่งรุ่น [[UIDevice currentDevice]] จะส่งคืนเฉพาะ "iPhone" หากแอปเปิดตัวจาก iOS Simulator
Stefan

ทำไมไม่-[NSString containsString]?
Gobe


0

คำตอบของฉันขึ้นอยู่กับ @Daniel Magnusson คำตอบและความคิดเห็นของ @Nuthatch และ @ n.Drake และฉันเขียนมันเพื่อประหยัดเวลาสำหรับผู้ใช้ที่ทำงานกับ iOS9 ขึ้นไป

นี่คือสิ่งที่ได้ผลสำหรับฉัน:

if UIDevice.currentDevice().name.hasSuffix("Simulator"){
    //Code executing on Simulator
} else{
    //Code executing on Device
}

1
รหัสจะไม่ทำงานหากผู้ใช้เพิ่มSimulatorคำในชื่ออุปกรณ์ของเขา
mbelsky

น่าเสียดายที่ XCode 8 UIDevice.current.nameรายงานชื่อของเครื่องที่ Simulator กำลังทำงานอยู่ (โดยทั่วไปจะคล้ายกับ "Simon's MacBook Pro" ในตอนนี้) ดังนั้นการทดสอบจึงไม่น่าเชื่อถือ ฉันยังคงมองหาวิธีที่สะอาดในการแก้ไข
ไมเคิล

0

/// คืนค่าจริงถ้าเครื่องจำลองไม่ใช่อุปกรณ์

public static var isSimulator: Bool {
    #if (arch(i386) || arch(x86_64)) && os(iOS)
        return true
    #else
        return false
    #endif
}

0

Apple ได้เพิ่มการสนับสนุนสำหรับการตรวจสอบแอพที่มีการกำหนดเป้าหมายสำหรับตัวจำลองดังต่อไปนี้:

#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif

0

หากไม่มีอะไรทำงานลองสิ่งนี้

public struct Platform {

    public static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0 // Use this line in Xcode 7 or newer
    }

}

-4

ในความคิดของฉันคำตอบ (นำเสนอด้านบนและทำซ้ำด้านล่าง):

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

เป็นคำตอบที่ดีที่สุดเพราะเห็นได้ชัดว่าดำเนินการที่ RUNTIME กับการเป็น DIRECTIVE แบบคอมไพล์


11
ฉันไม่เห็นด้วย. รหัสนี้จะสิ้นสุดลงในผลิตภัณฑ์ของคุณในขณะที่คอมไพเลอร์คำสั่งเก็บ - บนอุปกรณ์ที่ไม่จำเป็น - ประจำออก
เก้าก้อนหิน

1
คอมไพเลอร์ไดเรกทีฟทำงานเพราะอุปกรณ์และเครื่องจำลองเป็นเป้าหมายการรวบรวมที่แตกต่างกันอย่างสิ้นเชิง - นั่นคือคุณจะไม่ใช้ไบนารี่เดียวกันทั้งคู่ มันมีที่จะรวบรวมกับฮาร์ดแวร์ที่แตกต่างกันจึงทำให้ความรู้สึกในกรณีที่
Brad Parks

การดำเนินการที่ RUNTIME ทำให้เป็นคำตอบที่แย่ที่สุด
gnasher729

-4

สิ่งนี้ทำงานได้ดีที่สุดสำหรับฉัน

NSString *name = [[UIDevice currentDevice] name];


if ([name isEqualToString:@"iPhone Simulator"]) {

}

2
ใน Xcode 7.3 iPhone 6 Plus Simulator จะกลับ"iPhone"มา
Eric
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.