แนวทางปฏิบัติที่ดีที่สุดของ iOS Prefix.pch


90

ฉันเคยเห็นนักพัฒนาหลายคนที่เพิ่มมาโครอำนวยความสะดวกต่างๆให้กับ Prefix.pch ของโปรเจ็กต์ iOS ของพวกเขา

คุณแนะนำให้เพิ่มอะไร (หรือไม่) ในไฟล์ iOS Prefix.pch Prefix.pch ของคุณมีลักษณะอย่างไร


บล็อกโพสต์โดยละเอียดเกี่ยวกับเรื่อง: cimgf.com/2010/05/02/my-current-prefix-pch-file
hpique

2
เพียงใส่มาโครของคุณลงในไฟล์ส่วนหัวเช่นMacros.hจากนั้นนำเข้าไฟล์นี้ไปยังไฟล์prefix.pch.
Malloc

ฉันกำลังประสบปัญหาเดียวกัน ... วิธีแก้ไขใน Xcode 6.1
Yalamandarao

คำตอบ:


121

Ewww …อย่าใส่มาโครในไฟล์. pch! ไฟล์. pch คือตามความหมายของส่วนหัวที่คอมไพล์ล่วงหน้าเฉพาะโปรเจ็กต์ ไม่ควรใช้เกินบริบทของโครงการและไม่ควรมีอะไรเลยนอกจาก#includes และ#imports

ถ้าคุณมีแมโครบางและเช่นว่าคุณต้องการที่จะใช้ร่วมกันระหว่างส่วนหัวแล้วติด 'em ในไฟล์ส่วนหัวของตัวเอง - Common.hหรืออะไรก็ตาม - และ#include ที่จุดเริ่มต้นของ .pch ที่


คุณจะรวมอะไรไว้ใน Common.h นั้น?
hpique

4
ไม่มีอะไร; ฉันจะใส่ #defines ต่างๆเท่านั้น ฯลฯ ... ไว้ในนั้น
bbum

37

สำหรับ iOS ที่ทันสมัยและ OS X คนควรจะใช้โมดูล นี้จะเปิดใช้งานโดยเริ่มต้นสำหรับโครงการใหม่และนำเข้า / @importรวมสามารถทำได้โดยใช้

โมดูลอนุญาตให้คอมไพเลอร์สร้างการแสดงเนื้อหาของโมดูลในระดับกลาง (เช่นส่วนหัวของเฟรมเวิร์ก) เช่นเดียวกับ PCH การเป็นตัวแทนระดับกลางนี้อาจใช้ร่วมกันในการแปลหลาย ๆ แต่โมดูลจะก้าวไปอีกขั้นเพราะโมดูลไม่จำเป็นต้องกำหนดเป้าหมายเฉพาะและการประกาศไม่จำเป็นต้องแปลเป็นภาษาท้องถิ่น (ถึง a *.pch) การเป็นตัวแทนนี้สามารถช่วยคุณประหยัดงานคอมไพเลอร์ที่ซ้ำซ้อนได้

การใช้โมดูลคุณไม่จำเป็นต้องมี PCH และคุณอาจจะต้องกำจัดมันออกไปทั้งหมด - เพื่อเป็นการสนับสนุนการใช้@importLocal เพื่อการพึ่งพา ในกรณีนี้ PCH จะช่วยให้คุณไม่ต้องพิมพ์การรวมในเครื่องไปยังการอ้างอิง (ซึ่ง IMO ที่คุณควรทำต่อไป)

ตอนนี้หากเรามองย้อนกลับไปที่คำถามเดิม: คุณควรหลีกเลี่ยงการเติม PCH ของคุณด้วยสิ่งสุ่มทุกประเภท มาโครค่าคงที่#definesและไลบรารีเล็ก ๆ ทุกประเภท โดยทั่วไปแล้วคุณควรละเว้นสิ่งที่เป็นจริงไม่จำเป็นส่วนใหญ่ของไฟล์ที่มาของคุณ การใส่สิ่งของทุกประเภทลงใน PCH ของคุณเป็นเพียงการเพิ่มน้ำหนักและการพึ่งพา ฉันเห็นผู้คนใส่ทุกสิ่งที่พวกเขาเชื่อมโยงและอื่น ๆ ในปชช. ในความเป็นจริงเฟรมเวิร์กเสริมมักจะต้องมองเห็นได้ในการแปลบางส่วนเท่านั้นในกรณีส่วนใหญ่ เช่น "นี่คือข้อมูล StoreKit ของเรา - มานำเข้า StoreKit เฉพาะที่ที่ต้องใช้เท่านั้นมองเห็นได้ โดยเฉพาะอย่างยิ่งการแปลทั้ง 3 นี้ "ซึ่งจะช่วยลดเวลาในการสร้างของคุณและช่วยให้คุณติดตามการอ้างอิงของคุณเพื่อให้คุณสามารถใช้โค้ดซ้ำได้ง่ายขึ้นดังนั้นในโครงการ ObjC คุณมักจะหยุดที่ Foundation หากมีจำนวนมาก ของ UI จากนั้นคุณอาจพิจารณาเพิ่ม UIKit หรือ AppKit ใน PCH ของคุณทั้งหมดนี้สมมติว่าคุณต้องการเพิ่มประสิทธิภาพเวลาในการสร้างปัญหาอย่างหนึ่งของ PCH ขนาดใหญ่ที่รวมทุกอย่าง (เกือบ) ทุกอย่างคือการลบการอ้างอิงที่ไม่จำเป็นออกไปนั้นใช้เวลานานมากครั้งเดียว การอ้างอิงของโปรเจ็กต์ของคุณเพิ่มขึ้นและเวลาในการสร้างของคุณเพิ่มขึ้นคุณต้องต่อสู้กลับโดยการกำจัดการพึ่งพาที่ไม่จำเป็นเพื่อลดเวลาในการสร้างของคุณนอกจากนี้สิ่งที่เปลี่ยนแปลงมักจะถูกเก็บไว้ให้พ้นจาก PCH ของคุณการเปลี่ยนแปลงต้องการการสร้างใหม่ทั้งหมด มีตัวเลือกบางอย่างในการแบ่งปัน PCH หากคุณใช้ PCHs

เท่าที่ฉันใส่ไว้ใน PCH: ฉันหยุดใช้มันสำหรับเป้าหมายส่วนใหญ่เมื่อหลายปีก่อน มักจะมีไม่เพียงพอที่จะมีคุณสมบัติเหมือนกัน จำไว้ว่าฉันเขียน C ++, ObjC, ObjC ++ และ C - คอมไพเลอร์จะปล่อยหนึ่งสำหรับแต่ละ lang ในเป้าหมายของคุณ ดังนั้นการเปิดใช้งานจึงมักส่งผลให้เวลาคอมไพล์ช้าลงและ I / O สูงขึ้น ท้ายที่สุดแล้วการเพิ่มการพึ่งพาไม่ใช่วิธีที่ดีในการต่อสู้กับการพึ่งพาในโครงการที่ซับซ้อน การทำงานกับหลายภาษา / ภาษาถิ่นมีการเปลี่ยนแปลงมากมายในการอ้างอิงที่จำเป็นสำหรับเป้าหมายที่กำหนด ไม่ฉันจะไม่แนะนำว่าเหมาะสมที่สุดสำหรับทุกโครงการ แต่นั่นจะให้มุมมองบางประการเกี่ยวกับการจัดการการพึ่งพาในโครงการขนาดใหญ่


อ้างอิง


หมายเหตุ

  • เดิมคำถามนี้ถูกถามไม่กี่ปีก่อนการเปิดตัวโมดูล
  • ปัจจุบัน (Xcode 5.0) โมดูลทำงานสำหรับ C และ ObjC แต่ไม่ใช่ C ++

หมายความว่าอย่างไรเมื่อสร้างใหม่ทั้งหมดสำหรับโครงการที่เปิดใช้งานโมดูล คุณจะรู้ว่า -Swift.h bridging header ใหม่นี้ไม่ใช่ตัวเลือกที่เหมาะสมสำหรับ. pch แต่ฉันเคยเห็นคนทำแบบนั้น ดังที่คุณจะเห็นว่ามีใครทำเช่นนั้นหรือไม่ เรามีส่วนหัวที่เปลี่ยนแปลงอยู่เสมอใน. pch ดังนั้นมันจึงสร้างทุกอย่างในไฟล์. pch ทุกครั้งที่สร้าง -Swift.h คุณเห็นด้วยกับเรื่องนี้หรือไม่? คุณมีปัจจัยการผลิตเพิ่มเติมหรือไม่?
MadNik

@MadNik สมมติว่า PCH ของแอปของคุณมีส่วนหัวหลักของไลบรารี / เฟรมเวิร์กที่คุณกำลังพัฒนาอยู่ ตัวอย่างเช่น: #import "MyLib/MyLib.h". ทุกครั้งที่มีMyLib.hการเปลี่ยนแปลงไฟล์ต้นฉบับทุกไฟล์ในแอปจะต้องคอมไพล์ใหม่ หากคุณใช้ MyLib ในไฟล์ต้นฉบับเพียงไฟล์เดียวจะต้องคอมไพล์ไฟล์นั้นใหม่เมื่อ MyLib เปลี่ยนแปลง เชื่อหรือไม่ว่าทุกวันนี้ฉันไม่ได้ใช้ไฟล์ PCH เลย
justin

8

ฉันเห็นด้วยกับ bbum สิ่งที่ฉันใช้กับไฟล์ PCH คือมันควรมีเฉพาะ#includeหรือ#importคำสั่งเท่านั้น ดังนั้นหากคุณมีมาโครระดับสูงที่เป็นประโยชน์จำนวนมากให้กำหนดมันในสิ่งที่ชอบCommon.hและ#importไฟล์นั้นตามที่ bbum แนะนำ

ฉันมักจะไปขั้นตอนต่อไปและใช้แฟ้ม PCH เพื่อ#importไฟล์ที่เรียกว่าXXCategories.h(ในกรณีที่XXเป็นชั้นตั้งชื่อการประชุมคำนำหน้าคุณใช้) ที่มี#importสำหรับทุก UIKit และชั้นมูลนิธิของฉันประเภทNSString+XXAdditions.h, UIColor+XXAdditons.hฯลฯ


ฉันแค่อยากรู้ ในไฟล์. PCH ความแตกต่างระหว่างการนำเข้า Common.h ซึ่งมีหลากหลาย#importและนำเข้า#importโดยตรงคืออะไร สิ่งเหล่านี้จะไม่เหมือนกันหรือไม่หรือมีผลต่อประสิทธิภาพการทำงานหรือไม่?
Hlung

สำหรับความรู้ของฉันไม่มีความแตกต่างอย่างแท้จริง ฉันเดาว่ามันเป็นแนวทางปฏิบัติที่ดีที่สุด แทนที่จะใส่มาโครและสิ่งอื่น ๆ ลงในไฟล์ PCH ของคุณควรใช้สำหรับ#importและ#include.
CIFilter

1
ความแตกต่างคือการใช้งานซ้ำได้ ปชช. เป็นโครงการเฉพาะ Common.h จะเป็นเรื่องธรรมดาสำหรับหลาย ๆ โครงการ คล้ายกับการถามว่าทำไมคุณไม่ใส่คลาส util ทั้งหมดลงในโปรเจ็กต์แทนที่จะสร้างโปรเจ็กต์ย่อยที่คุณสามารถใช้ซ้ำได้ แม้ว่าจะเป็นตัวอย่างที่สร้างขึ้นเนื่องจากเป็นเพียงการคัดลอกวางธรรมดา ...
bandejapaisa

6

สร้างไฟล์ส่วนหัว "macros.h"

นำเข้าส่วนหัวนี้ไปยัง Prefix.pch

ใน macros.h นี้ใส่กรอบและสิ่งสำคัญอื่น ๆ ทั้งหมด

หากคุณกังวลเกี่ยวกับประสิทธิภาพไม่ต้องกังวลดูสิ่งที่ Apple พูด:

ส่วนหัวและประสิทธิภาพ

หากคุณกังวลว่าการรวมไฟล์ส่วนหัวหลักอาจทำให้โปรแกรมของคุณขยายตัวไม่ต้องกังวล เนื่องจากอินเทอร์เฟซ OS X ถูกนำไปใช้โดยใช้เฟรมเวิร์กโค้ดสำหรับอินเทอร์เฟซเหล่านั้นจึงอยู่ในไลบรารีที่แบ่งใช้แบบไดนามิกและไม่อยู่ในไฟล์ปฏิบัติการ นอกจากนี้รหัสที่ใช้โดยโปรแกรมของคุณเท่านั้นที่เคยโหลดลงในหน่วยความจำขณะรันไทม์ดังนั้นรอยในหน่วยความจำของคุณจึงมีขนาดเล็กเช่นเดียวกัน สำหรับการรวมไฟล์ส่วนหัวจำนวนมากในระหว่างการคอมไพล์อีกครั้งไม่ต้องกังวล Xcode มีสิ่งอำนวยความสะดวกส่วนหัวที่คอมไพล์ไว้ล่วงหน้าเพื่อเร่งเวลาในการคอมไพล์ การรวบรวมส่วนหัวของกรอบงานทั้งหมดในครั้งเดียวไม่จำเป็นต้องคอมไพล์ส่วนหัวใหม่เว้นแต่คุณจะเพิ่มกรอบงานใหม่ ในระหว่างนี้คุณสามารถใช้อินเทอร์เฟซใดก็ได้จากเฟรมเวิร์กที่ให้มาโดยมีโทษประสิทธิภาพเพียงเล็กน้อยหรือไม่มีเลย

ในมาโครของฉันด้วยฉันใส่ค่าคงที่จำนวนมากเช่น:

// delegate
#define UIAppDelegate (AppDelegate *)[[UIApplication sharedApplication] delegate]
#define APPDELEGATE   ((AppDelegate *)[[UIApplication sharedApplication] delegate])

// system
#define IS_IPHONE_4INCH (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone && [UIScreen mainScreen].bounds.size.height==568)
#define IS_IPAD                     (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

// screen size
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_IPHONE_4 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0)
#define IS_IPHONE_5 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
#define IS_IPHONE_6 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
#define IS_IPHONE_6PLUS (IS_IPHONE && [[UIScreen mainScreen] nativeScale] == 3.0f)
#define IS_IPHONE_6_PLUS (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0)
#define IS_RETINA ([[UIScreen mainScreen] scale] == 2.0)
#define IS_RETINA_DISPLAY ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))
#define IS_PORTRAIT                 UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])
#define IS_LANDSCAPE                UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])

//system version
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)

// math
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))

// cores
#define RGB(r,g,b)    [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
#define RGBA(r,g,b,a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a]
#define MAKECOLOR(R, G, B, A) [UIColor colorWithRed:((float)R/255.0f) green:((float)G/255.0f) blue:((float)B/255.0f) alpha:A]
#define MAKECOLORFROMHEX(hexValue) [UIColor colorWithRed: ((float)((hexValue & 0xFF0000) >> 16))/255.0 green:((float)((hexValue & 0xFF00) >> 8))/255.0 blue:((float)(hexValue & 0xFF))/255.0 alpha:1.0]



//customizations
#define SHOW_STATUS_BAR               [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
#define HIDE_STATUS_BAR               [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];

#define SHOW_NAVIGATION_BAR           [self.navigationController setNavigationBarHidden:FALSE];
#define HIDE_NAVIGATION_BAR           [self.navigationController setNavigationBarHidden:TRUE];

#define VC_OBJ(x) [[x alloc] init]
#define VC_OBJ_WITH_NIB(x) [[x alloc] initWithNibName : (NSString *)CFSTR(#x) bundle : nil]

#define RESIGN_KEYBOARD [[[UIApplication sharedApplication] keyWindow] endEditing:YES];

#define CLEAR_NOTIFICATION_BADGE                       [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
#define REGISTER_APPLICATION_FOR_NOTIFICATION_SERVICE  [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]

#define HIDE_NETWORK_ACTIVITY_INDICATOR                 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
#define SHOW_NETWORK_ACTIVITY_INDICATOR                 [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

2
มีประโยชน์อีกอย่างที่นี่: #define async(...) dispatch_async(dispatch_get_main_queue(), __VA_ARGS__ )... เพื่อเรียกใช้บล็อกบนเธรดหลัก:async(^{ self.someLabel.text = @":D"; });
Alejandro Iván
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.