วิธีการป้องกันใน Objective-C


112

เทียบเท่ากับวิธีการป้องกันใน Objective-C คืออะไร? ฉันต้องการกำหนดวิธีการที่เฉพาะคลาสที่ได้รับเท่านั้นที่สามารถเรียก / ใช้งานได้

คำตอบ:


47

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

ที่นำมาจากแหล่งที่มา


ในทางเทคนิคคุณไม่สามารถเลียนแบบตัวแปรส่วนตัวได้
Sharen Eayrs

ลี - ถ้าคุณประกาศฟังก์ชันพอยน์เตอร์ใน @protected และกำหนดฟังก์ชันในวิธีการ init มันจะใช้ได้ไหม
bikram990

156

คุณสามารถจำลองการเข้าถึงวิธีการที่มีการป้องกันและแบบส่วนตัวได้โดยทำดังต่อไปนี้:

  • ประกาศวิธีการส่วนตัวของคุณในส่วนขยายคลาส (เช่นหมวดหมู่ที่ไม่มีชื่อที่ประกาศไว้ใกล้ด้านบนสุดของไฟล์คลาส '.m)
  • ประกาศวิธีการป้องกันของคุณในส่วนหัวคลาสย่อย - Apple ใช้รูปแบบนี้กับ UIGestureRecognizer (ดูเอกสารประกอบและการอ้างอิงถึง UIGestureRecognizerSubclass.h)

การป้องกันเหล่านี้ไม่ได้เป็นไปตามที่ Sachin ระบุไว้บังคับใช้ที่รันไทม์ (เช่นใน Java เป็นต้น)


2
เกี่ยวกับโซลูชันที่คล้ายกับ UIGestureRecognizer: ปัญหาคือหากบางรหัสนำเข้าคลาสย่อยก็จะนำเข้าส่วนหัวของคลาสย่อยด้วยดังนั้นจึงจะสามารถเข้าถึงเมธอด "ป้องกัน" ได้ มีวิธีการประมาณนั้นหรือไม่?
yonix

5
สวัสดีคุณ yonix การนำเข้าสำหรับส่วนหัวคลาสย่อยจะทำภายในไฟล์. m ที่ไม่อยู่ภายในไฟล์. h ดังนั้นการนำเข้าคลาสย่อยจะไม่นำเข้าเมธอดที่มีการป้องกันเหล่านี้
Brian Westphal

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

1
ฉันจะดู UIGestureRecognizerSubclass.h ได้อย่างไร
Sharen Eayrs

5
ขอขอบคุณที่ชี้ให้เห็นว่า Apple ดำเนินการภายในอย่างไร ฉันโพสต์ตัวอย่างเต็มรูปแบบของวิธีการใช้งานสิ่งต่างๆเช่นเดียวกับ Apple ในUIGestureRecognizerSubclass.h
Dirty Henry

14

นี่คือสิ่งที่ฉันทำเพื่อให้เมธอดป้องกันมองเห็นได้สำหรับคลาสย่อยของฉันโดยไม่ต้องใช้เมธอด นั่นหมายความว่าฉันไม่ได้รับคำเตือนของคอมไพเลอร์ในคลาสย่อยของฉันเกี่ยวกับการใช้งานที่ไม่สมบูรณ์

SuperClassProtectedMethods.h (ไฟล์โปรโตคอล):

@protocol SuperClassProtectedMethods <NSObject>
- (void) protectMethod:(NSObject *)foo;
@end

@interface SuperClass (ProtectedMethods) < SuperClassProtectedMethods >
@end

SuperClass.m: (ตอนนี้คอมไพเลอร์จะบังคับให้คุณเพิ่มวิธีการป้องกัน)

#import "SuperClassProtectedMethods.h"
@implementation SuperClass
- (void) protectedMethod:(NSObject *)foo {}
@end

SubClass.m:

#import "SuperClassProtectedMethods.h"
// Subclass can now call the protected methods, but no external classes importing .h files will be able to see the protected methods.

2
ความหมายของการป้องกันคือไม่สามารถเรียกจากภายนอกได้ คุณยังคงสามารถเรียกใช้เมธอดใด ๆ ที่กำหนดไว้ในคลาสได้ไม่ว่าจะมองเห็นจากภายนอกหรือไม่ก็ตาม
eonil

ใช่ฉันเข้าใจสิ่งนี้ วิธีนี้ใช้ได้กับสมองของมนุษย์ไม่ใช่โค้ดที่คอมไพล์จริง แต่ Objective-C ไม่อนุญาตให้ทำเช่นนั้น (ไม่สามารถโทรจากภายนอกได้) คุณสามารถเสมอperformSelectorกับมัน
Michael Kernahan

1
[(id)obj hiddenMethod]นอกจากนี้คุณยังสามารถทำ พูดอย่างถูกต้องว่า Objective-C ไม่รองรับวิธีการป้องกัน
eonil

ปัญหานี้คือคลาสที่ได้รับการป้องกันของคุณไม่สามารถโฆษณาคุณสมบัติได้ หากคุณไม่ต้องการคุณสมบัติใคร ๆ ก็ทราบดีว่าคุณสามารถเพิ่มหมวดหมู่ที่มีการป้องกันได้
Sharen Eayrs

@eonil: "คุณสามารถทำ [(id) obj hiddenMethod] ได้ด้วย" ใช่คุณสามารถทำได้ แต่คุณจะได้รับคำเตือนจากคอมไพเลอร์หากวิธีนั้นไม่ได้อยู่ในอินเทอร์เฟซใด ๆ ที่รวมอยู่
Kaiserludi

9

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

/////// SuperClass.h
@interface SuperClass

@end

/////// SuperClass.m
@implementation SuperClass
- (void) protectedMethod
{}
@end

/////// SubClass.h
@interface SubClass : SuperClass
@end

/////// SubClass.m
@interface SubClass (Protected)
- (void) protectedMethod ;
@end

@implementation SubClass
- (void) callerOfProtectedMethod
{
  [self protectedMethod] ; // this will not generate warning
} 
@end

2
ในกรณีนี้คอมไพเลอร์ยังคงเตือนเกี่ยวกับวิธีการที่ไม่ได้ใช้งานprotectedMethod
skywinder

วิธีนี้เป็นวิธีที่ดี แต่แทนที่จะสร้างหมวดหมู่ (ป้องกัน) คุณสามารถสร้างส่วนขยายได้
Dharmesh Siddhpura

@skywinder อาจจะทำได้ในเวอร์ชันก่อนหน้า แต่ Xcode เวอร์ชันปัจจุบันไม่มีปัญหากับโซลูชันนี้
Darren Ehlers

2

อีกวิธีหนึ่งโดยใช้ตัวแปร @protected

@interface SuperClass:NSObject{
  @protected
    SEL protectedMehodSelector;
}

- (void) hackIt;
@end

@implementation SuperClass

-(id)init{

self = [super init];
if(self) {
 protectedMethodSelector = @selector(baseHandling);
 }

return self;
}

- (void) baseHandling {

  // execute your code here
}

-(void) hackIt {

  [self performSelector: protectedMethodSelector];
}

@end

@interface SubClass:SuperClass
@end

@implementation SubClass

-(id)init{

self = [super init];
if(self) {
 protectedMethodSelector = @selector(customHandling);
 }

return self;
}

- (void) customHandling {

  // execute your custom code here
}

@end

และคุณสามารถใส่ IVars ที่ได้รับการป้องกันไว้ในส่วนขยายคลาสในไฟล์ส่วนหัวที่ชื่อว่า protected ด้วย
malhal

1

คุณสามารถกำหนดเมธอดเป็นเมธอดส่วนตัวของคลาสพาเรนต์และสามารถใช้[super performSelector:@selector(privateMethod)];ในคลาสลูกได้


0

คุณสามารถจัดเรียงได้ด้วยหมวดหมู่

@interface SomeClass (Protected)
-(void)doMadProtectedThings;
@end

@implementation SomeClass (Protected)

- (void)doMadProtectedThings{
    NSLog(@"As long as the .h isn't imported into a class of completely different family, these methods will never be seen. You have to import this header into the subclasses of the super instance though.");
}

@end

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

วิธีที่ดีที่สุดที่จะไปน่าจะเป็นหมวดหมู่ต่อเนื่องของคลาสตามที่ @Brian Westphal ตอบ แต่คุณจะต้องกำหนดวิธีการในหมวดหมู่นี้ใหม่สำหรับแต่ละอินสแตนซ์ย่อย


0

ทางเลือกหนึ่งคือใช้ส่วนขยายคลาสเพื่อซ่อนเมธอด

ใน.h:

@interface SomeAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

ใน.m:

@interface SomeAppDelegate()
- (void)localMethod;
@end

@implementation SomeAppDelegate

- (void)localMethod
{
}

@end

ฉันไม่คิดว่าคุณต้องการการ@interfaceประกาศในไฟล์. m ด้วยซ้ำ คุณสามารถประกาศฟังก์ชันและใช้งานได้และจะถือว่าเป็นส่วนตัว
Russ

1
โปรดทราบว่านี่เป็นจริงเฉพาะกับการอัปเดตล่าสุดใน Objective C ก่อนหน้านั้นคุณต้องประกาศวิธีการในอินเทอร์เฟซมิฉะนั้นอย่างน้อยคุณจะได้รับคำเตือน
Guillaume Laurent

0

ฉันมักจะตั้งชื่อวิธีการป้องกันด้วยคำนำหน้าภายใน:

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