जवाबों:
आप -addSomeClass:
कंपाइल-टाइम स्टैटिक टाइप चेकिंग की अनुमति देने के लिए एक विधि के साथ एक श्रेणी बना सकते हैं (इसलिए कंपाइलर आपको बता सकता है कि क्या आप उस वस्तु को जोड़ने की कोशिश करते हैं जो यह जानता है कि उस विधि के माध्यम से एक अलग वर्ग है), लेकिन इसे लागू करने का कोई वास्तविक तरीका नहीं है एक सरणी में केवल किसी दिए गए वर्ग की वस्तुएँ होती हैं।
सामान्य तौर पर, उद्देश्य-सी में इस तरह की बाधा की आवश्यकता नहीं लगती है। मुझे नहीं लगता कि मैंने कभी उस सुविधा के लिए एक अनुभवी कोको प्रोग्रामर को सुना है। केवल वे लोग जो अन्य भाषाओं के प्रोग्रामर लगते हैं जो अभी भी उन भाषाओं में सोच रहे हैं। यदि आप किसी दिए गए वर्ग की वस्तुओं को किसी सरणी में चाहते हैं, तो केवल उस वर्ग की वस्तुओं को वहाँ चिपकाएँ। यदि आप यह परीक्षण करना चाहते हैं कि आपका कोड ठीक से व्यवहार कर रहा है, तो उसका परीक्षण करें।
किसी ने इसे अभी तक यहाँ नहीं रखा है, इसलिए मैं इसे करूँगा!
Tthis अब आधिकारिक तौर पर ऑब्जेक्टिव-सी में समर्थित है। Xcode 7 के अनुसार, आप निम्न सिंटैक्स का उपयोग कर सकते हैं:
NSArray<MyClass *> *myArray = @[[MyClass new], [MyClass new]];
ध्यान दें
यह ध्यान रखना महत्वपूर्ण है कि ये केवल संकलक चेतावनी हैं और आप तकनीकी रूप से किसी भी वस्तु को अपने सरणी में सम्मिलित कर सकते हैं। ऐसी लिपियाँ उपलब्ध हैं जो सभी चेतावनियों को त्रुटियों में बदल देती हैं जो निर्माण को रोकती हैं।
nonnull
XCode 6 में उपयोग कर सकते हैं और जहां तक मुझे याद है, वे एक ही समय में पेश किए गए थे। इसके अलावा, क्या इस तरह की अवधारणाओं का उपयोग XCode संस्करण या iOS संस्करण पर निर्भर करता है?
@property (nonatomic, strong) NSArray<id<SomeProtocol>>* protocolObjects;
थोड़ा भद्दा लगता है, लेकिन चाल है!
यह दृढ़ता से टाइप की भाषाओं (जैसे C ++ या जावा) से संक्रमण करने वाले लोगों के लिए एक अपेक्षाकृत सामान्य प्रश्न है, जो पायथन, रूबी, या ऑब्जेक्टिव-सी जैसी अधिक कमजोर या गतिशील रूप से टाइप की गई भाषाओं में है। ऑब्जेक्टिव-सी में, अधिकांश ऑब्जेक्ट्स NSObject
(प्रकार id
) से विरासत में प्राप्त होते हैं ( बाकी अन्य मूल वर्ग से विरासत में मिलते हैं जैसे कि NSProxy
और टाइप भी हो सकते हैं id
), और किसी भी वस्तु को कोई भी संदेश भेजा जा सकता है। बेशक, एक उदाहरण के लिए एक संदेश भेजना कि यह नहीं पहचानता है एक रनटाइम त्रुटि का कारण हो सकता है (और एक संकलक चेतावनी का कारण भी होगा उपयुक्त-डब्ल्यू झंडे के साथ किया )। जब तक एक उदाहरण आपके द्वारा भेजे गए संदेश का जवाब देता है, तब तक आप परवाह नहीं कर सकते हैं कि वह किस वर्ग का है। इसे अक्सर "बतख टाइपिंग" के रूप में संदर्भित किया जाता है क्योंकि "अगर यह एक बतख की तरह बन्द हो जाता है [अर्थात एक चयनकर्ता को जवाब देता है], यह एक बतख है [अर्थात यह संदेश को संभाल सकता है; जो परवाह करता है कि यह किस वर्ग का है]"।
आप परीक्षण कर सकते हैं कि क्या कोई उदाहरण -(BOOL)respondsToSelector:(SEL)selector
विधि के साथ रन टाइम पर चयनकर्ता को जवाब देता है । मान लें कि आप एक सरणी में हर मामले पर एक विधि कॉल करना चाहते हैं, लेकिन यकीन नहीं है कि सभी उदाहरणों संदेश संभाल कर सकते हैं (ताकि आप सिर्फ उपयोग नहीं कर सकते हैं NSArray
की -[NSArray makeObjectsPerformSelector:]
, कुछ इस तरह काम करेगा:
for(id o in myArray) {
if([o respondsToSelector:@selector(myMethod)]) {
[o myMethod];
}
}
यदि आप उन इंस्टेंस के लिए स्रोत कोड को नियंत्रित करते हैं जो उस पद्धति को लागू करते हैं जिसे आप कॉल करना चाहते हैं, तो अधिक सामान्य दृष्टिकोण यह होगा @protocol
कि उन तरीकों को परिभाषित किया जाए और घोषित किया जाए कि प्रश्न में कक्षाएं उनके प्रोटोकॉल में घोषणा को लागू करती हैं। इस उपयोग में, एक @protocol
जावा इंटरफेस या एक सी + + अमूर्त आधार वर्ग के अनुरूप है। फिर आप प्रत्येक विधि की प्रतिक्रिया के बजाय पूरे प्रोटोकॉल के अनुरूप परीक्षण कर सकते हैं। पिछले उदाहरण में, इससे बहुत फर्क नहीं पड़ेगा, लेकिन अगर आप कई तरीके बता रहे हैं, तो यह चीजों को सरल बना सकता है। उदाहरण तब होगा:
for(id o in myArray) {
if([o conformsToProtocol:@protocol(MyProtocol)]) {
[o myMethod];
}
}
यह मानते हुए कि MyProtocol
वाणी myMethod
। यह दूसरा दृष्टिकोण इष्ट है क्योंकि यह पहले से अधिक कोड के इरादे को स्पष्ट करता है।
अक्सर, इन तरीकों में से एक आपको परवाह करने से मुक्त करता है कि क्या एक सरणी में सभी ऑब्जेक्ट एक दिए गए प्रकार के हैं। यदि आप अभी भी देखभाल करते हैं, तो मानक गतिशील भाषा दृष्टिकोण इकाई परीक्षण, इकाई परीक्षण, इकाई परीक्षण है। क्योंकि इस आवश्यकता में एक प्रतिगमन एक (संभावित अप्राप्य) रनटाइम (संकलित समय नहीं) त्रुटि उत्पन्न करेगा, आपको व्यवहार को सत्यापित करने के लिए परीक्षण कवरेज की आवश्यकता होगी ताकि आप किसी क्रैश को जंगली में जारी न करें। इस स्थिति में, एक ऑपरेशन को सुधारें जो सरणी को संशोधित करता है, फिर सत्यापित करें कि सरणी के सभी उदाहरण किसी दिए गए वर्ग के हैं। उचित परीक्षण कवरेज के साथ, आपको उदाहरण पहचान को सत्यापित करने के अतिरिक्त रनटाइम ओवरहेड की भी आवश्यकता नहीं है। आपके पास अच्छी इकाई परीक्षण कवरेज है, है न?
id
एस का उपयोग नहीं करते हैं , जहां आवश्यक हो, सिवाय इसके कि जावा कोडर्स Object
एस के आसपास से गुजरते हैं । क्यों नहीं? यदि आपको यूनिट टेस्ट मिल गए हैं तो इसकी आवश्यकता नहीं है? क्योंकि यह वहाँ है और टाइप किए गए सरणियों के रूप में आपके कोड को अधिक बनाए रखने योग्य बनाता है। ऐसा लगता है कि मंच में निवेश किए गए लोग एक बिंदु को स्वीकार नहीं करना चाहते हैं, और इसलिए कारणों का आविष्कार करना कि यह चूक वास्तव में एक लाभ है।
आप NSMutableArray
प्रकार की सुरक्षा को लागू करने के लिए उपवर्ग बना सकते हैं ।
NSMutableArray
एक वर्ग क्लस्टर है , इसलिए उपवर्ग तुच्छ नहीं है। मैंने NSArray
उस वर्ग के अंदर एक सरणी से इनहेरिट करना और इनवॉइस अग्रेषित करना समाप्त कर दिया । परिणाम एक वर्ग कहा जाता है ConcreteMutableArray
जो है उपवर्ग करने के लिए आसान। यहाँ मैं क्या लेकर आया हूँ:
अद्यतन: एक वर्ग क्लस्टर उपवर्ग पर माइक ऐश से इस ब्लॉग पोस्ट की जाँच करें ।
अपनी परियोजना में उन फ़ाइलों को शामिल करें, फिर मैक्रो का उपयोग करके अपनी इच्छा के अनुसार किसी भी प्रकार का उत्पादन करें:
MyArrayTypes.h
CUSTOM_ARRAY_INTERFACE(NSString)
CUSTOM_ARRAY_INTERFACE(User)
MyArrayTypes.m
CUSTOM_ARRAY_IMPLEMENTATION(NSString)
CUSTOM_ARRAY_IMPLEMENTATION(User)
उपयोग:
NSStringArray* strings = [NSStringArray array];
[strings add:@"Hello"];
NSString* str = [strings get:0];
[strings add:[User new]]; //compiler error
User* user = [strings get:0]; //compiler error
अन्य विचार
NSArray
क्रमबद्धता / deserialization का समर्थन करने के लिएअपने स्वाद के आधार पर, आप जेनेरिक विधियों को ओवरराइड / छुपाना चाह सकते हैं
- (void) addObject:(id)anObject
ऑब्जेक्टिव-सी के लिए https://github.com/tomersh/Objective-C-Generics , एक संकलन-समय (प्रीप्रोसेसर-कार्यान्वित) जेनरिक कार्यान्वयन पर एक नज़र है। इस ब्लॉग पोस्ट में एक अच्छा अवलोकन है। मूल रूप से आपको संकलन-समय की जाँच (चेतावनी या त्रुटियाँ) मिलती हैं, लेकिन जेनरिक के लिए कोई रनटाइम जुर्माना नहीं।
यह गीथब प्रोजेक्ट वास्तव में उस कार्यक्षमता को लागू करता है।
आप तब उपयोग कर सकते हैं <>
कोष्ठक का हैं, जैसे आप C # में करेंगे।
उनके उदाहरणों से:
NSArray<MyClass>* classArray = [NSArray array];
NSString *name = [classArray lastObject].name; // No cast needed
एक संभावित तरीका NSArray को उपवर्गित कर सकता है लेकिन Apple इसे न करने की सलाह देता है। टाइप किए गए NSArray की वास्तविक आवश्यकता के बारे में दो बार सोचना आसान है।
मैंने एक NSArray उपवर्ग बनाया, जो NSArray के वर्ग-क्लस्टर प्रकृति के साथ समस्याओं से बचने के लिए ivar के रूप में एक NSArray ऑब्जेक्ट का उपयोग कर रहा है। किसी ऑब्जेक्ट को जोड़ने या अस्वीकार करने में ब्लॉक लगते हैं।
केवल NSString ऑब्जेक्ट को अनुमति देने के लिए, आप एक के AddBlock
रूप में परिभाषित कर सकते हैं
^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
}
आप यह निर्धारित करने के FailBlock
लिए क्या कर सकते हैं कि क्या करना है, अगर कोई तत्व परीक्षण में विफल हो गया - फ़िल्टर करने के लिए इनायत से विफल हो, इसे किसी अन्य सरणी में जोड़ें, या - यह डिफ़ॉल्ट है - एक अपवाद बढ़ाएं।
VSBlockTestedObjectArray.h
#import <Foundation/Foundation.h>
typedef BOOL(^AddBlock)(id element);
typedef void(^FailBlock)(id element);
@interface VSBlockTestedObjectArray : NSMutableArray
@property (nonatomic, copy, readonly) AddBlock testBlock;
@property (nonatomic, copy, readonly) FailBlock failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock Capacity:(NSUInteger)capacity;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock;
@end
VSBlockTestedObjectArray.m
#import "VSBlockTestedObjectArray.h"
@interface VSBlockTestedObjectArray ()
@property (nonatomic, retain) NSMutableArray *realArray;
-(void)errorWhileInitializing:(SEL)selector;
@end
@implementation VSBlockTestedObjectArray
@synthesize testBlock = _testBlock;
@synthesize failBlock = _failBlock;
@synthesize realArray = _realArray;
-(id)initWithCapacity:(NSUInteger)capacity
{
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock
FailBlock:(FailBlock)failBlock
Capacity:(NSUInteger)capacity
{
self = [self initWithCapacity:capacity];
if (self) {
_testBlock = [testBlock copy];
_failBlock = [failBlock copy];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock
{
return [self initWithTestBlock:testBlock FailBlock:failBlock Capacity:0];
}
-(id)initWithTestBlock:(AddBlock)testBlock
{
return [self initWithTestBlock:testBlock FailBlock:^(id element) {
[NSException raise:@"NotSupportedElement" format:@"%@ faild the test and can't be add to this VSBlockTestedObjectArray", element];
} Capacity:0];
}
- (void)dealloc {
[_failBlock release];
[_testBlock release];
self.realArray = nil;
[super dealloc];
}
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if(self.testBlock(anObject))
[self.realArray insertObject:anObject atIndex:index];
else
self.failBlock(anObject);
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[self.realArray removeObjectAtIndex:index];
}
-(NSUInteger)count
{
return [self.realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [self.realArray objectAtIndex:index];
}
-(void)errorWhileInitializing:(SEL)selector
{
[NSException raise:@"NotSupportedInstantiation" format:@"not supported %@", NSStringFromSelector(selector)];
}
- (id)initWithArray:(NSArray *)anArray { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfFile:(NSString *)aPath{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfURL:(NSURL *)aURL{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(id)firstObj, ... { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { [self errorWhileInitializing:_cmd]; return nil;}
@end
इसका उपयोग इस तरह करें:
VSBlockTestedObjectArray *stringArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
} FailBlock:^(id element) {
NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSString", element);
}];
VSBlockTestedObjectArray *numberArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSNumber class]];
} FailBlock:^(id element) {
NSLog(@"%@ can't be added, didn't pass the test. It is not an object of class NSNumber", element);
}];
[stringArray addObject:@"test"];
[stringArray addObject:@"test1"];
[stringArray addObject:[NSNumber numberWithInt:9]];
[stringArray addObject:@"test2"];
[stringArray addObject:@"test3"];
[numberArray addObject:@"test"];
[numberArray addObject:@"test1"];
[numberArray addObject:[NSNumber numberWithInt:9]];
[numberArray addObject:@"test2"];
[numberArray addObject:@"test3"];
NSLog(@"%@", stringArray);
NSLog(@"%@", numberArray);
यह सिर्फ एक उदाहरण कोड है और वास्तविक दुनिया के आवेदन में कभी इस्तेमाल नहीं किया गया था। ऐसा करने के लिए संभवत: इसे लागू करने के लिए मोर NSArray विधि की जरूरत है।
यदि आप c ++ और ऑब्जेक्टिव-सी (यानी मिमी फ़ाइल प्रकार का उपयोग करते हैं) को मिलाते हैं, तो आप जोड़ी या टुपल का उपयोग करके टाइपिंग को लागू कर सकते हैं। उदाहरण के लिए, निम्न विधि में, आप एक सी ++ ऑब्जेक्ट प्रकार std :: pair बना सकते हैं, इसे OC आवरण प्रकार (std के रैपर :: जोड़ी जिसे आपको परिभाषित करने की आवश्यकता है) के ऑब्जेक्ट में कनवर्ट करें, और फिर इसे कुछ को पास करें अन्य OC विधि, जिसके भीतर आपको OC ऑब्जेक्ट को उपयोग करने के लिए C ++ ऑब्जेक्ट में वापस कनवर्ट करने की आवश्यकता है। OC विधि केवल OC आवरण प्रकार को स्वीकार करती है, इस प्रकार प्रकार की सुरक्षा सुनिश्चित करती है। तुम भी प्रकार, सुरक्षा के सुविधा के लिए और अधिक उन्नत सी ++ सुविधाओं का लाभ उठाने के लिए टपल, वैरेडिक टेम्पलेट, टाइपिस्ट का उपयोग कर सकते हैं।
- (void) tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) indexPath
{
std::pair<UITableView*, NSIndexPath*> tableRow(tableView, indexPath);
ObjCTableRowWrapper* oCTableRow = [[[ObjCTableRowWrapper alloc] initWithTableRow:tableRow] autorelease];
[self performSelector:@selector(selectRow:) withObject:oCTableRow];
}