หมวดหมู่ Objective-C ในห้องสมุดคงที่


153

คุณช่วยแนะนำวิธีการเชื่อมโยงไลบรารีคงที่กับโครงการ iPhone ให้ถูกต้องได้ไหม ฉันใช้โครงการห้องสมุดแบบสแตติกที่เพิ่มเข้ากับโครงการแอปเป็นการพึ่งพาโดยตรง (เป้าหมาย -> ทั่วไป -> การพึ่งพาโดยตรง) และใช้งานได้ดีทั้งหมดยกเว้นหมวดหมู่ หมวดหมู่ที่กำหนดในไลบรารีแบบคงที่ไม่ทำงานในแอป

ดังนั้นคำถามของฉันคือวิธีเพิ่มไลบรารีแบบคงที่ที่มีบางหมวดหมู่ในโครงการอื่น

และโดยทั่วไปวิธีปฏิบัติที่ดีที่สุดที่จะใช้ในรหัสโครงการแอปจากโครงการอื่นคืออะไร


1
ดีพบคำตอบและดูเหมือนว่าคำถามนี้ตอบแล้วที่นี่ (ขออภัยพลาดstackoverflow.com/questions/932856/… )
Vladimir

คำตอบ:


228

วิธีแก้ไข:จาก Xcode 4.2 คุณจะต้องไปที่แอปพลิเคชันที่เชื่อมโยงกับไลบรารี (ไม่ใช่ไลบรารี) และคลิกโครงการใน Project Navigator คลิกเป้าหมายของแอปแล้วสร้างการตั้งค่าแล้วค้นหา "อื่น ๆ ลิงเกอร์ธง "คลิกปุ่ม + และเพิ่ม '-ObjC' '-all_load' และ '-force_load' ไม่จำเป็นอีกต่อไป

รายละเอียด: ฉันพบคำตอบในฟอรัมบล็อกและ Apple เอกสารต่างๆ ตอนนี้ฉันลองทำการสรุปสั้น ๆ เกี่ยวกับการค้นหาและการทดสอบของฉัน

ปัญหาเกิดจาก (การอ้างอิงจากแอปเปิ้ลถามตอบด้านเทคนิค QA1490 https://developer.apple.com/library/content/qa/qa1490/_index.html ):

Objective-C ไม่ได้กำหนดสัญลักษณ์ลิงเกอร์สำหรับแต่ละฟังก์ชั่น (หรือวิธีการใน Objective-C) - แต่สัญลักษณ์ลิงเกอร์ถูกสร้างขึ้นสำหรับแต่ละคลาสเท่านั้น หากคุณขยายคลาสที่มีอยู่แล้วด้วยหมวดหมู่ตัวเชื่อมโยงไม่ทราบว่าจะเชื่อมโยงรหัสวัตถุของการใช้คลาสหลักและการใช้หมวดหมู่ สิ่งนี้จะป้องกันวัตถุที่สร้างในแอปพลิเคชันที่เป็นผลลัพธ์จากการตอบสนองต่อตัวเลือกที่กำหนดไว้ในหมวดหมู่

และการแก้ปัญหาของพวกเขา:

เมื่อต้องการแก้ไขปัญหานี้ไลบรารีแบบคงที่ควรผ่านตัวเลือก -ObjC ไปยังตัวเชื่อมโยง การตั้งค่าสถานะนี้ทำให้ linker โหลดทุกวัตถุไฟล์ในไลบรารีที่กำหนดคลาสหรือหมวดหมู่ Objective-C ในขณะที่ตัวเลือกนี้โดยทั่วไปจะส่งผลให้เกิดการปฏิบัติการที่มีขนาดใหญ่ขึ้น (เนื่องจากรหัสวัตถุเพิ่มเติมโหลดลงในแอปพลิเคชัน) แต่จะช่วยให้การสร้างไลบรารีสแตติก Objective-C ที่สำเร็จมีประสิทธิภาพ

และยังมีคำแนะนำในคำถามที่พบบ่อยเกี่ยวกับการพัฒนา iPhone:

ฉันจะเชื่อมโยงคลาส Objective-C ทั้งหมดในไลบรารีแบบคงที่ได้อย่างไร ตั้งค่าการตั้งค่าสถานะบิลเซอร์อื่น ๆ เป็น -ObjC

คำอธิบายและธง:

- all_loadโหลดสมาชิกทั้งหมดของไลบรารีไฟล์เก็บถาวรแบบคงที่

- ObjCโหลดสมาชิกทั้งหมดของไลบรารีไฟล์เก็บถาวรแบบคงที่ที่ใช้คลาส Objective-C หรือหมวดหมู่

- force_load (path_to_archive)โหลดสมาชิกทั้งหมดของห้องสมุดเก็บคงที่ระบุ หมายเหตุ: -all_load บังคับให้สมาชิกทั้งหมดของไฟล์เก็บถาวรทั้งหมดถูกโหลด ตัวเลือกนี้ช่วยให้คุณสามารถกำหนดเป้าหมายเป็นที่เก็บถาวรที่เฉพาะเจาะจง

* เราสามารถใช้ force_load เพื่อลดขนาดไบนารีของแอปและเพื่อหลีกเลี่ยงความขัดแย้งซึ่ง all_load อาจทำให้เกิดในบางกรณี

ใช่ใช้ได้กับไฟล์ * .a ที่เพิ่มเข้าในโครงการ แต่ฉันมีปัญหากับโครงการ lib เพิ่มเป็นการพึ่งพาโดยตรง แต่ต่อมาฉันพบว่ามันเป็นความผิดของฉัน - โครงการพึ่งพาโดยตรงอาจไม่ได้เพิ่มอย่างถูกต้อง เมื่อฉันลบมันและเพิ่มอีกครั้งด้วยขั้นตอน:

  1. ลากและวางไฟล์โครงการ lib ในโครงการแอพ (หรือเพิ่มด้วย Project-> เพิ่มในโครงการ ... )
  2. คลิกที่ลูกศรที่ไอคอนโครงการ lib - ชื่อไฟล์ mylib.a ปรากฏขึ้นลากไฟล์ mylib.a นี้และวางลงในเป้าหมาย -> เชื่อมโยงไบนารีกับกลุ่มไลบรารี
  3. เปิดข้อมูลเป้าหมายในหน้ากำปั้น (ทั่วไป) และเพิ่ม lib ของฉันลงในรายการการพึ่งพา

หลังจากนั้นทุกอย่างก็โอเค แฟล็ก "-ObjC" เพียงพอในกรณีของฉัน

ฉันยังสนใจแนวคิดจากhttp://iphonedevelopmentexperiences.blogspot.com/2010/03/categories-in-static-library.htmlบล็อก ผู้เขียนบอกว่าเขาสามารถใช้หมวดหมู่จาก lib โดยไม่ต้องตั้งค่าแฟล็ก -all_load หรือ -ObjC เขาเพิ่งเพิ่มไปยังหมวดหมู่ไฟล์ h / m อินเทอร์เฟซ dummy คลาสว่าง / การใช้งานเพื่อบังคับให้ลิงเกอร์ใช้ไฟล์นี้ และใช่เคล็ดลับนี้ทำงาน

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

ดังนั้นถ้าเราเขียนไฟล์ lib เป็น:

// mylib.h
void useMyLib();

@interface NSObject (Logger)
-(void)logSelf;
@end


// mylib.m
void useMyLib(){
    NSLog(@"do nothing, just for make mylib linked");
}


@implementation NSObject (Logger)
-(void)logSelf{
    NSLog(@"self is:%@", [self description]);
}
@end

และถ้าเราเรียกใช้ useMyLib (); ทุกที่ในโครงการแอพจากนั้นในชั้นเรียนใด ๆ เราสามารถใช้วิธีการหมวดหมู่ logSelf;

[self logSelf];

และบล็อกเพิ่มเติมเกี่ยวกับชุดรูปแบบ:

http://t-machine.org/index.php/2009/10/13/how-to-make-an-iphone-static-library-part-1/

http://blog.costan.us/2009/12/fat-iphone-static-libraries-device-and.html


8
ดูเหมือนว่าจะมีการแก้ไขบันทึกเทคโนโลยีของ Apple เพื่อพูดว่า "เพื่อแก้ไขปัญหานี้การเชื่อมโยงเป้าหมายกับไลบรารีคงที่จะต้องผ่านตัวเลือก -ObjC ไปยังตัวเชื่อมโยง" ซึ่งตรงกันข้ามกับสิ่งที่ยกมาข้างต้น เราเพิ่งยืนยันว่าคุณจะต้องรวมเมื่อเชื่อมโยงแอปและไม่ใช่ห้องสมุด
Ken Aspeslagh

จากข้อมูลของ doc developer.apple.com/library/mac/#qa/qa1490/_index.htmlเราควรใช้แฟล็ก -all_load หรือ -force_load ดังกล่าว linker มีข้อบกพร่องใน 64 บิต Mac App และ iPhone App "สำคัญ: สำหรับแอปพลิเคชัน 64 บิตและ iPhone OS มีข้อผิดพลาดตัวเชื่อมโยงที่ป้องกัน -ObjC จากการโหลดไฟล์วัตถุจากไลบรารีคงที่ที่มีเพียงหมวดหมู่และไม่มีคลาสวิธีแก้ปัญหาคือใช้แฟล็ก -all_load หรือ -force_load"
Robin

2
@ Ken Aspelagh: ขอบคุณฉันมีปัญหาเดียวกัน แฟล็ก -ObjC และ -all_load ต้องถูกเพิ่มเข้ากับแอปเองไม่ใช่ไลบรารี่
titaniumdecoy

3
คำตอบที่ดีแม้ว่าผู้มาใหม่สำหรับคำถามนี้ควรทราบว่ามันล้าสมัยแล้ว ลองดูคำตอบstackoverflow.com/a/9224606/322748 ของ tonklon (ไม่จำเป็นต้องใช้ all_load / force_load อีกต่อไป)
Jay Peyer

ฉันติดอยู่กับสิ่งเหล่านี้เป็นเวลาเกือบครึ่งชั่วโมงและด้วยการทดลองและข้อผิดพลาดฉันเพิ่งทำมันออกมา ด้วยวิธีใด ๆ ขอบคุณ คำตอบนี้มีค่าเท่ากับ +1 และคุณได้สิ่งนั้น !!!
Deepukjayan

118

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

คอมไพเลอร์แปลงไฟล์ต้นฉบับ (.c, .cc, .cpp, .m) เป็นไฟล์วัตถุ (.o) มีหนึ่งอ็อบเจ็กต์ไฟล์ต่อไฟล์ต้นฉบับ ไฟล์วัตถุมีสัญลักษณ์รหัสและข้อมูล ไฟล์วัตถุไม่สามารถใช้งานได้โดยตรงจากระบบปฏิบัติการ

ตอนนี้เมื่อสร้าง dynamic library (.dylib) เฟรมเวิร์กบันเดิลที่โหลดได้ (.bundle) หรือไบนารีที่ปฏิบัติการไฟล์อ็อบเจ็กต์เหล่านี้จะเชื่อมโยงเข้าด้วยกันโดย linker เพื่อสร้างสิ่งที่ระบบปฏิบัติการพิจารณาว่า "ใช้งานได้" เช่นบางสิ่งบางอย่าง โหลดโดยตรงไปยังที่อยู่หน่วยความจำที่เฉพาะเจาะจง

อย่างไรก็ตามเมื่อสร้างไลบรารีแบบสแตติกไฟล์อ็อบเจ็กต์เหล่านี้ทั้งหมดจะถูกเพิ่มลงในไฟล์เก็บถาวรขนาดใหญ่ดังนั้นส่วนขยายของสแตติกไลบรารี (.a สำหรับไฟล์เก็บถาวร) ดังนั้นไฟล์. a จึงไม่มีอะไรมากกว่าไฟล์เก็บถาวรของไฟล์ object (.o) นึกถึงไฟล์เก็บถาวร TAR หรือไฟล์ ZIP โดยไม่บีบอัด มันง่ายกว่าที่จะคัดลอกไฟล์. a ไฟล์หนึ่งไฟล์ที่อยู่รอบ ๆ ไฟล์. o (คล้ายกับ Java ที่คุณเก็บไฟล์. class ไว้ในไฟล์. jar เพื่อการแจกจ่ายที่ง่าย)

เมื่อทำการเชื่อมโยงไบนารีไปยังไลบรารีแบบสแตติก (= ไฟล์เก็บถาวร) ตัวลิงก์จะได้รับตารางของสัญลักษณ์ทั้งหมดในไฟล์เก็บถาวรและตรวจสอบว่าสัญลักษณ์ใดที่อ้างอิงโดยไบนารี เฉพาะอ็อบเจ็กต์ไฟล์ที่มีสัญลักษณ์อ้างอิงเท่านั้นที่ถูกโหลดโดย linker และถูกพิจารณาโดยกระบวนการลิงก์ เช่นถ้าไฟล์เก็บถาวรของคุณมีไฟล์วัตถุ 50 ไฟล์ แต่มีเพียง 20 ไฟล์ที่มีสัญลักษณ์ที่ใช้โดยไบนารีเฉพาะไฟล์ 20 เท่านั้นที่โหลดโดยลิงเกอร์ไฟล์อื่น ๆ 30 ไฟล์จะถูกละเว้นในกระบวนการลิงก์

วิธีนี้ใช้งานได้ดีสำหรับรหัส C และ C ++ เนื่องจากภาษาเหล่านี้พยายามทำมากที่สุดเท่าที่จะเป็นไปได้ในเวลารวบรวม (แม้ว่า C ++ จะมีคุณสมบัติแบบรันไทม์เท่านั้น) อย่างไรก็ตาม Obj-C เป็นภาษาที่แตกต่าง Obj-C ขึ้นอยู่กับคุณสมบัติของรันไทม์เป็นอย่างมากและคุณสมบัติ Obj-C จำนวนมากนั้นเป็นคุณสมบัติของรันไทม์เท่านั้น คลาส Obj-C มีสัญลักษณ์เทียบเท่ากับฟังก์ชัน C หรือตัวแปร C ทั่วโลก (อย่างน้อยใน Obj-C runtime ปัจจุบัน) ตัวเชื่อมโยงสามารถดูได้ว่ามีการอ้างอิงคลาสหรือไม่ดังนั้นจึงสามารถพิจารณาว่ามีการใช้งานคลาสหรือไม่ หากคุณใช้คลาสจากไฟล์อ็อบเจ็กต์ในไลบรารีแบบสแตติกไฟล์อ็อบเจ็กต์นี้จะถูกโหลดโดยตัวลิงก์เนื่องจากตัวลิงก์จะเห็นสัญลักษณ์กำลังถูกใช้งาน หมวดหมู่เป็นคุณสมบัติแบบรันไทม์เท่านั้นหมวดหมู่ไม่ใช่สัญลักษณ์เช่นคลาสหรือฟังก์ชันและนั่นหมายความว่าตัวเชื่อมโยงไม่สามารถระบุได้ว่าหมวดหมู่นั้นใช้งานอยู่หรือไม่

หากลิงเกอร์โหลดไฟล์อ็อบเจ็กต์ที่มีรหัส Obj-C ชิ้นส่วน Obj-C ทั้งหมดของมันจะเป็นส่วนหนึ่งของระยะการลิงก์เสมอ ดังนั้นหากไฟล์ออบเจ็กต์ที่มีหมวดหมู่ถูกโหลดเนื่องจากสัญลักษณ์ใด ๆ จากมันถูกพิจารณาว่า "ใช้งาน" (ไม่ว่าจะเป็นคลาสไม่ว่าจะเป็นฟังก์ชั่นไม่ว่าจะเป็นตัวแปรทั่วโลก) หมวดหมู่ก็จะโหลดเช่นกัน . แต่ถ้าไม่ได้โหลดออบเจ็กต์ไฟล์เองหมวดหมู่ในนั้นจะไม่สามารถใช้งานได้ในขณะทำงาน ไฟล์อ็อบเจ็กต์ที่มีหมวดหมู่เท่านั้นไม่เคยถูกโหลดเนื่องจากไม่มีสัญลักษณ์ที่ linker เคยพิจารณาว่า "กำลังใช้" และนี่คือปัญหาทั้งหมดที่นี่

มีการเสนอแนวทางแก้ไขหลายอย่างแล้วและตอนนี้คุณก็รู้ว่าการเล่นทั้งหมดนี้เข้าด้วยกันได้อย่างไรมาดูการแก้ปัญหาที่เสนออีกครั้ง:

  1. ทางออกหนึ่งคือการเพิ่ม-all_loadการโทรลิงเกอร์ ตัวเชื่อมโยงนั้นตั้งค่าสถานะอะไรจริง " โหลดไฟล์ออบเจ็กต์ทั้งหมดของไฟล์เก็บถาวรทั้งหมดโดยไม่คำนึงว่าคุณเห็นสัญลักษณ์ใด ๆ ที่ใช้งานอยู่หรือไม่ 'แน่นอนว่ามันใช้งานได้ แต่มันอาจสร้างไบนารีที่ค่อนข้างใหญ่

  2. อีกวิธีคือการเพิ่ม-force_loadการเรียก linker รวมถึงเส้นทางไปยังที่เก็บถาวร แฟล็กนี้ทำงานเหมือน-all_loadกันทุกประการ แต่สำหรับไฟล์เก็บถาวรที่ระบุเท่านั้น แน่นอนว่ามันจะได้ผลเช่นกัน

  3. ทางออกที่นิยมที่สุดคือการเพิ่ม-ObjCการโทรลิงเกอร์ ตัวเชื่อมโยงนั้นตั้งค่าสถานะอะไรจริง แฟล็กนี้บอกตัวลิงก์ " โหลดไฟล์อ็อบเจ็กต์ทั้งหมดจากไฟล์เก็บถาวรทั้งหมดหากคุณเห็นว่ามีโค้ด Obj-C ใด ๆ " และ "รหัส Obj-C ใด ๆ " รวมถึงหมวดหมู่ สิ่งนี้จะทำงานได้ดีและจะไม่บังคับให้โหลดไฟล์ออบเจ็กต์ที่ไม่มีรหัส Obj-C (ซึ่งยังคงโหลดตามความต้องการเท่านั้น)

  4. Perform Single-Object Prelinkวิธีการแก้ปัญหาอีกประการหนึ่งคือการสร้างการตั้งค่า Xcode ค่อนข้างใหม่ การตั้งค่านี้จะทำอะไร หากเปิดใช้งานไฟล์ออบเจ็กต์ทั้งหมด (จำไว้ว่ามีหนึ่งไฟล์ต่อไฟล์ต้นฉบับ) จะถูกรวมเข้าด้วยกันเป็นไฟล์ออบเจ็กต์เดียว (นั่นไม่ใช่การเชื่อมโยงจริงดังนั้นชื่อPreLink ) และไฟล์ออบเจ็กต์เดี่ยวนี้ ไฟล์ ") จะถูกเพิ่มลงในไฟล์เก็บถาวร หากตอนนี้มีการพิจารณาสัญลักษณ์ใด ๆ ของไฟล์วัตถุต้นแบบไฟล์วัตถุหลักทั้งหมดจะถูกนำมาใช้และทำให้ชิ้นส่วน Objective-C ทั้งหมดของมันถูกโหลดอยู่เสมอ และเนื่องจากคลาสเป็นสัญลักษณ์ปกติก็พอที่จะใช้คลาสเดียวจากไลบรารีแบบสแตติกเพื่อรับหมวดหมู่ทั้งหมดได้เช่นกัน

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

นั่นคือคนทั้งหมด

โอ้รอมีอีกสิ่งหนึ่งที่: ลิงเกอร์มีตัวเลือกในการตั้งชื่อ
-dead_stripตัวเลือกนี้ทำอะไร หาก linker ตัดสินใจโหลดไฟล์อ็อบเจ็กต์สัญลักษณ์ทั้งหมดของอ็อบเจ็กต์ไฟล์จะกลายเป็นส่วนหนึ่งของไบนารีที่เชื่อมโยงไม่ว่าจะถูกใช้หรือไม่ก็ตาม เช่นไฟล์วัตถุที่มี 100 ฟังก์ชั่น แต่มีเพียงหนึ่งในนั้นจะถูกใช้โดยไบนารีฟังก์ชั่นทั้ง 100 ยังคงถูกเพิ่มลงในไบนารีเพราะไฟล์วัตถุจะถูกเพิ่มโดยรวมหรือไม่ได้เพิ่มเลย การเพิ่มไฟล์วัตถุบางส่วนโดยปกติแล้ว linkers ไม่รองรับ

อย่างไรก็ตามหากคุณบอกลิงเกอร์ว่า "เดตสตริป" ตัวลิงก์จะเพิ่มไฟล์ออบเจ็กต์ทั้งหมดลงในไบนารีแก้ไขการอ้างอิงทั้งหมดและสุดท้ายสแกนไบนารีสำหรับสัญลักษณ์ที่ไม่ได้ใช้งาน (หรือใช้โดยสัญลักษณ์อื่นที่ไม่ใช่ ใช้). สัญลักษณ์ทั้งหมดที่พบว่าไม่ได้ใช้งานจะถูกลบออกจากนั้นเป็นส่วนหนึ่งของขั้นตอนการปรับให้เหมาะสม ในตัวอย่างข้างต้นฟังก์ชันที่ไม่ได้ใช้ 99 ฟังก์ชันจะถูกลบออกอีกครั้ง นี้จะเป็นประโยชน์มากถ้าคุณใช้ตัวเลือกเช่น-load_all, -force_loadหรือPerform Single-Object Prelinkเพราะตัวเลือกเหล่านี้สามารถระเบิดขนาดไบนารีอย่างมากในบางกรณีและปอกตายจะลบรหัสไม่ได้ใช้และข้อมูลอีกครั้ง

การลอกแบบ Dead ทำงานได้ดีมากสำหรับรหัส C (เช่นฟังก์ชั่นที่ไม่ได้ใช้ตัวแปรและค่าคงที่จะถูกลบออกตามที่คาดไว้) และใช้งานได้ค่อนข้างดีสำหรับ C ++ (เช่นการลบคลาสที่ไม่ได้ใช้) มันไม่สมบูรณ์แบบในบางกรณีสัญลักษณ์บางอย่างไม่ได้ถูกลบแม้ว่ามันจะโอเคที่จะลบออก แต่ในกรณีส่วนใหญ่มันใช้งานได้ดีสำหรับภาษาเหล่านี้

สิ่งที่เกี่ยวกับ Obj-C ลืมมันซะ! ไม่มีการลอกแบบตายสำหรับ Obj-C เนื่องจาก Obj-C เป็นภาษาคุณลักษณะรันไทม์คอมไพเลอร์ไม่สามารถพูดได้ในเวลาคอมไพล์ว่ามีการใช้งานสัญลักษณ์หรือไม่ เช่นคลาส Obj-C ไม่ได้ใช้งานหากไม่มีรหัสอ้างอิงโดยตรงถูกต้องหรือไม่ ไม่ถูกต้อง! คุณสามารถสร้างสตริงที่มีชื่อคลาสแบบไดนามิกขอตัวชี้คลาสสำหรับชื่อนั้นและจัดสรรคลาสได้แบบไดนามิก เช่นแทน

MyCoolClass * mcc = [[MyCoolClass alloc] init];

ฉันยังสามารถเขียน

NSString * cname = @"CoolClass";
NSString * cnameFull = [NSString stringWithFormat:@"My%@", cname];
Class mmcClass = NSClassFromString(cnameFull);
id mmc = [[mmcClass alloc] init];

ในทั้งสองกรณีmmcเป็นการอ้างอิงไปยังวัตถุของคลาส "MyCoolClass" แต่ไม่มีการอ้างอิงโดยตรงไปยังคลาสนี้ในตัวอย่างรหัสที่สอง (ไม่แม้แต่ชื่อคลาสเป็นสแตติกสตริง) ทุกอย่างเกิดขึ้นที่รันไทม์เท่านั้น และถึงแม้ว่าชั้นเรียนจะเป็นสัญลักษณ์ที่แท้จริง มันยิ่งแย่ลงสำหรับหมวดหมู่เพราะพวกเขาไม่ได้เป็นสัญลักษณ์ที่แท้จริง

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

เช่นถ้าคุณต้องการหมวดหมู่สำหรับ NSData เช่นการเพิ่มวิธีการบีบอัด / คลายการบีบอัดให้คุณสร้างไฟล์ส่วนหัว:

// NSData+Compress.h
@interface NSData (Compression)
    - (NSData *)compressedData;
    - (NSData *)decompressedData;
@end

void import_NSData_Compression ( );

และไฟล์การใช้งาน

// NSData+Compress
@implementation NSData (Compression)
    - (NSData *)compressedData 
    {
        // ... magic ...
    }

    - (NSData *)decompressedData
    {
        // ... magic ...
    }
@end

void import_NSData_Compression ( ) { }

ตอนนี้ให้ตรวจสอบให้แน่ใจว่าimport_NSData_Compression()มีการเรียกใช้รหัสใด ๆ ไม่สำคัญว่าจะถูกเรียกหรือความถี่ที่ถูกเรียก ที่จริงแล้วมันไม่จำเป็นต้องถูกเรียกเลยมันก็เพียงพอแล้วหากลิงเกอร์คิดเช่นนั้น เช่นคุณสามารถใส่รหัสต่อไปนี้ได้ทุกที่ในโครงการของคุณ:

__attribute__((used)) static void importCategories ()
{
    import_NSData_Compression();
    // add more import calls here
}

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

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


1
ขอบคุณสำหรับการกล่าวถึง-whyloadพยายามดีบักสาเหตุที่ตัวเชื่อมโยงทำอะไรบางอย่างอาจเป็นเรื่องยาก!
Ben S

มีตัวเลือกคือในDead Code Stripping Build Settings>Linkingมันเหมือนกับที่-dead_stripเพิ่มเข้ามาOther Linker Flagsหรือไม่?
Xiao

1
@Sean ใช่มันเหมือนกัน เพียงแค่อ่าน "ความช่วยเหลือด่วน" ที่มีอยู่สำหรับการตั้งค่าการสร้างทุกคำตอบอยู่ที่นั่น: postimg.org/image/n7megftnr/full
Mecki

@Mecki ขอบคุณ ผมพยายามที่จะกำจัด-ObjCดังนั้นฉันพยายามสับของคุณ "import_NSString_jsonObject()", referenced from: importCategories() in main.o ld: symbol(s) not foundแต่บ่น ฉันใส่import_NSString_jsonObjectในกรอบฝังตัวของฉันชื่อUtilityและเพิ่ม#import <Utility/Utility.h>กับคำสั่งในตอนท้ายของฉัน__attribute__ AppDelegate.h
เซี่ยว

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

24

ปัญหานี้ได้รับการแก้ไขใน LLVM การจัดส่งเป็นส่วนหนึ่งของ LLVM 2.9 เวอร์ชัน Xcode แรกที่มีการแก้ไขคือ Xcode 4.2 การจัดส่งด้วย LLVM 3.0 การใช้-all_loadหรือ-force_loadไม่ต้องการอีกต่อไปเมื่อทำงานกับ XCode 4.2 -ObjCยังคงต้องการ


คุณแน่ใจเกี่ยวกับเรื่องนี้? ฉันกำลังทำงานในโครงการ iOS โดยใช้ Xcode 4.3.2 รวบรวมด้วย LLVM 3.1 และนี่ก็ยังเป็นปัญหาสำหรับฉัน
Ashley Mills

ตกลงนั่นเป็นความไม่แน่นอนเล็กน้อย -ObjCธงยังคงจำเป็นและมักจะเป็น วิธีแก้ปัญหาคือการใช้งานของหรือ-all_load -force_loadและนั่นไม่จำเป็นอีกต่อไป ฉันแก้ไขคำตอบของฉันด้านบน
tonklon

มีข้อเสียใด ๆ ในการรวมแฟล็ก -all_load (แม้ว่าไม่จำเป็น)? มันส่งผลกระทบต่อการรวบรวม / เปิดตัวในเวลาใด ๆ ?
ZS

ฉันกำลังทำงานกับ Xcode เวอร์ชัน 4.5 (4G182) และแฟล็ก -ObjC จะย้ายข้อผิดพลาดตัวเลือกที่ไม่รู้จักของฉันจากการพึ่งพาของบุคคลที่สามฉันพยายามที่จะใช้ในสิ่งที่ดูเหมือนความลึกของรันไทม์ Objective C: "- [__ NSArrayM map :]: ตัวเลือกที่ไม่รู้จักถูกส่งไปยังอินสแตนซ์ ... " เบาะแสใด ๆ
Robert Atkins

16

นี่คือสิ่งที่คุณต้องทำเพื่อแก้ไขปัญหานี้อย่างสมบูรณ์เมื่อรวบรวมไลบรารีสแตติกของคุณ:

ไปที่การตั้งค่า Xcode Build และตั้งค่าดำเนินการ Single-Object Prelink เป็น YES หรือ GENERATE_MASTER_OBJECT_FILE = YESในไฟล์กำหนดค่า build

โดยค่าเริ่มต้น linker สร้างไฟล์. o สำหรับแต่ละไฟล์. m ดังนั้นหมวดหมู่จึงได้รับไฟล์. o ที่แตกต่างกัน เมื่อ linker ดูที่ไฟล์. o ไลบรารีแบบคงที่มันจะไม่สร้างดัชนีของสัญลักษณ์ทั้งหมดต่อคลาส (รันไทม์จะไม่สำคัญอะไร)

คำสั่งนี้จะขอให้ลิงเกอร์รวบรวมวัตถุทั้งหมดเข้าด้วยกันเป็นไฟล์. o ไฟล์ใหญ่และด้วยวิธีนี้มันจะบังคับให้ลิงเกอร์ที่ประมวลผลไลบรารีคงที่เพื่อรับดัชนีหมวดหมู่ของคลาสทั้งหมด

หวังว่าชัดเจน


การแก้ไขนี้สำหรับฉันโดยไม่ต้องเพิ่ม -ObjC ไปยังเป้าหมายการลิงก์
Matthew Crenshaw

หลังจากอัปเดตเป็นไลบรารีรุ่นล่าสุดของBlocksKitฉันต้องใช้การตั้งค่านี้เพื่อแก้ไขปัญหา (ฉันใช้แฟล็ก -ObjC แล้วแต่ยังเห็นปัญหาอยู่)
rakmoh

1
จริงๆแล้วคำตอบของคุณไม่ถูกต้องนัก ฉันไม่ได้ "ขอให้ linker แพ็คหมวดหมู่ทั้งหมดของคลาสเดียวกันเข้าด้วยกันเป็นไฟล์. o หนึ่งไฟล์" มันขอให้ linker เชื่อมโยงไฟล์ออบเจ็กต์ทั้งหมด (.o) ลงในไฟล์อ็อบเจกต์ขนาดใหญ่เดียวก่อนสร้างห้องสมุดคงที่ พวกเขา / มัน เมื่อมีการอ้างอิงสัญลักษณ์ใด ๆ จากไลบรารีสัญลักษณ์ทั้งหมดจะถูกโหลด อย่างไรก็ตามสิ่งนี้จะไม่ทำงานหากไม่มีการอ้างอิงสัญลักษณ์ (เช่นถ้ามันจะไม่ทำงานหากมีเพียงหมวดหมู่ในห้องสมุด)
Mecki

ฉันไม่คิดว่ามันจะใช้ได้ผลถ้าคุณเพิ่มหมวดหมู่ไปยังคลาสที่มีอยู่เช่น NSData
Bob Whiteman

ฉันก็มีปัญหาในการเพิ่มหมวดหมู่ให้กับคลาสที่มีอยู่ ปลั๊กอินของฉันไม่สามารถจดจำได้ในขณะใช้งาน
David Dunham

9

ปัจจัยหนึ่งที่ถูกกล่าวถึงไม่ค่อยเมื่อใดก็ตามที่การอภิปรายห้องสมุดคงเชื่อมโยงขึ้นมาคือความจริงที่ว่าคุณยังจะต้องมีประเภทของตัวเองในการสร้าง phases-> คัดลอกไฟล์และแหล่งรวบรวมของห้องสมุดคงที่ของตัวเอง

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

ฉันใช้เวลาทั้งวันในการลองใช้งานรูปแบบต่าง ๆ ของ -objC และ -all_load ฯลฯ .. แต่ไม่มีอะไรออกมาจากมัน .. คำถามนี้นำเรื่องนี้มาสู่ความสนใจของฉัน (อย่าเข้าใจฉันผิด .. คุณยังต้องทำ -objC .. แต่มันมากกว่านั้น)

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


-1

คุณอาจต้องมีหมวดหมู่ในส่วนหัว "สาธารณะ" ของไลบรารีแบบคงที่: #import "MyStaticLib.h"

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