Tüm öğelerin türde NSMutableArray
olduğu bir örnek oluşturabilir miyim ?SomeClass
Yanıtlar:
-addSomeClass:
Derleme zamanı statik tip kontrolüne izin veren bir yöntemle bir kategori oluşturabilirsiniz (böylece derleyici, bu yöntem aracılığıyla farklı bir sınıf olduğunu bildiği bir nesneyi eklemeye çalışırsanız size bildirebilir), ancak bunu zorlamanın gerçek bir yolu yoktur. bir dizi yalnızca belirli bir sınıfın nesnelerini içerir.
Genel olarak, Objective-C'de böyle bir kısıtlamaya ihtiyaç yok gibi görünüyor. Deneyimli bir Kakao programcısının bu özellik için dilediğini hiç duymadım. Diğer dillerden programcılar gibi görünen tek kişi, hala bu dilleri düşünüyor. Bir dizide yalnızca belirli bir sınıfın nesnelerini istiyorsanız, oraya yalnızca o sınıfın nesnelerini yapıştırın. Kodunuzun düzgün çalışıp çalışmadığını test etmek istiyorsanız test edin.
Henüz kimse bunu buraya koymadı, bu yüzden yapacağım!
Tthis artık resmi olarak Objective-C'de destekleniyor. Xcode 7'den itibaren aşağıdaki sözdizimini kullanabilirsiniz:
NSArray<MyClass *> *myArray = @[[MyClass new], [MyClass new]];
Not
Bunların yalnızca derleyici uyarıları olduğunu ve teknik olarak yine de dizinize herhangi bir nesneyi ekleyebileceğinizi unutmamak önemlidir. Tüm uyarıları oluşturmayı engelleyecek hatalara dönüştüren komut dosyaları mevcuttur.
nonnull
XCode 6'da kullanabiliriz ve hatırladığım kadarıyla aynı anda tanıtıldılar. Ayrıca, bu tür kavramların kullanımı XCode sürümüne mi yoksa iOS sürümüne mi bağlı?
@property (nonatomic, strong) NSArray<id<SomeProtocol>>* protocolObjects;
Biraz hantal görünüyor, ama hile yapıyor!
Bu, güçlü tip dillerden (C ++ veya Java gibi) Python, Ruby veya Objective-C gibi daha zayıf veya dinamik olarak yazılmış dillere geçiş yapan kişiler için nispeten yaygın bir sorudur. Objective-C'de, çoğu nesne NSObject
(type id
) ' dan miras alır ( geri kalanı, type gibi başka bir kök sınıfından miras alır NSProxy
ve ayrıca tür olabilir id
) ve herhangi bir nesneye herhangi bir mesaj gönderilebilir. Elbette, tanımadığı bir örneğe mesaj göndermek çalışma zamanı hatasına neden olabilir (ve ayrıca bir derleyici uyarısına neden olur.uygun -W bayrakları ile). Bir örnek gönderdiğiniz mesaja yanıt verdiği sürece, hangi sınıfa ait olduğu umurunuzda olmayabilir. Bu genellikle "ördek yazma" olarak adlandırılır, çünkü "ördek gibi şaka yapıyorsa [yani bir seçiciye yanıt veriyorsa], bir ördekdir [yani mesajı işleyebilir; hangi sınıf olduğunu kimin umursadığı]".
-(BOOL)respondsToSelector:(SEL)selector
Yöntemle , bir örneğin çalışma zamanında bir seçiciye yanıt verip vermediğini test edebilirsiniz . Bir dizideki her örnekte bir yöntem çağırmak istediğinizi, ancak tüm örneklerin mesajı işleyebileceğinden emin olmadığınızı varsayarsak (bu nedenle yalnızca NSArray
's kullanamazsınız -[NSArray makeObjectsPerformSelector:]
, bunun gibi bir şey çalışır:
for(id o in myArray) {
if([o respondsToSelector:@selector(myMethod)]) {
[o myMethod];
}
}
Çağırmak istediğiniz yöntem (ler) i uygulayan örnekler için kaynak kodunu kontrol ederseniz, daha yaygın olan yaklaşım @protocol
, bu yöntemleri içeren bir yöntemi tanımlamak ve söz konusu sınıfların bildirimlerinde bu protokolü uyguladığını bildirmek olacaktır. Bu kullanımda a @protocol
, Java Arayüzüne veya C ++ soyut temel sınıfına benzer. Daha sonra her yönteme yanıt vermek yerine tüm protokole uygunluğu test edebilirsiniz. Önceki örnekte, pek bir fark yaratmazdı, ancak birden çok yöntemi çağırıyor olsaydınız, işleri basitleştirebilirdi. Örnek şu şekilde olacaktır:
for(id o in myArray) {
if([o conformsToProtocol:@protocol(MyProtocol)]) {
[o myMethod];
}
}
varsayarsak MyProtocol
beyan eder myMethod
. Bu ikinci yaklaşım, kodun amacını ilkinden daha fazla netleştirdiği için tercih edilir.
Genellikle, bu yaklaşımlardan biri sizi bir dizideki tüm nesnelerin belirli bir türde olup olmadığına aldırmaktan kurtarır. Hala umursuyorsanız, standart dinamik dil yaklaşımı birim testi, birim testi, birim testi yapmaktır. Bu gereksinimdeki bir gerileme, (muhtemelen kurtarılamaz) bir çalışma zamanı (derleme zamanı değil) hatası üreteceğinden, bir çökmeyi vahşice serbest bırakmamak için davranışı doğrulamak için test kapsamına sahip olmanız gerekir. Bu durumda, diziyi değiştiren bir işlem hazırlayın, ardından dizideki tüm örneklerin belirli bir sınıfa ait olduğunu doğrulayın. Uygun test kapsamı ile, örnek kimliğini doğrulamak için ek çalışma zamanı ek yüküne bile ihtiyacınız yoktur. Birim testi kapsamınız iyi, değil mi?
id
s kullanmadığınıza bahse girerim , Java kodlayıcıları Object
s'nin etrafından geçmez . Neden olmasın? Birim testleriniz varsa buna ihtiyacınız yok mu? Çünkü oradadır ve yazılan dizilerde olduğu gibi kodunuzu daha sürdürülebilir kılar. Görünüşe göre insanlar platforma yatırım yapmış ve bir noktayı kabul etmek istemiyorlar ve bu nedenle bu ihmalin aslında bir fayda olduğuna dair nedenler icat ediyorlar.
NSMutableArray
Tür güvenliğini sağlamak için alt sınıf yapabilirsiniz .
NSMutableArray
bir sınıf kümesidir , bu nedenle alt sınıflandırma önemsiz değildir. Sonunda NSArray
çağrıları o sınıf içindeki bir diziden devraldım ve bu diziye ilettim. Sonuç olarak adlandırılan bir sınıftır olan alt sınıf kolay. İşte bulduğum şey:ConcreteMutableArray
Güncelleme: Mike Ash'in bir sınıf kümesini alt sınıflandırma hakkındaki bu blog gönderisine göz atın.
Bu dosyaları projenize dahil edin, ardından makroları kullanarak dilediğiniz türleri oluşturun:
MyArrayTypes.h
CUSTOM_ARRAY_INTERFACE(NSString)
CUSTOM_ARRAY_INTERFACE(User)
MyArrayTypes.m
CUSTOM_ARRAY_IMPLEMENTATION(NSString)
CUSTOM_ARRAY_IMPLEMENTATION(User)
Kullanım:
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
diğer düşünceler
NSArray
Serileştirmeyi / serileştirmeyi desteklemek için'den miras alırZevkinize bağlı olarak, genel yöntemleri geçersiz kılmak / gizlemek isteyebilirsiniz:
- (void) addObject:(id)anObject
Objective-C için bir derleme zamanı (önişlemci tarafından uygulanmış) jenerik uygulaması olan https://github.com/tomersh/Objective-C-Generics'e bir göz atın . Bu blog yazısı güzel bir genel bakışa sahip. Temel olarak derleme zamanı denetimi (uyarılar veya hatalar) alırsınız, ancak jenerikler için çalışma süresi cezası olmaz.
Bu Github Projesi tam olarak bu işlevi yerine getiriyor .
Daha sonra <>
, C # 'da olduğu gibi parantezleri kullanabilirsiniz .
Örneklerinden:
NSArray<MyClass>* classArray = [NSArray array];
NSString *name = [classArray lastObject].name; // No cast needed
Muhtemel bir yol NSArray'i alt sınıflamak olabilir, ancak Apple bunu yapmamayı önerir. Tipik bir NSArray için gerçek ihtiyacın iki katını düşünmek daha kolaydır.
NSArray'in sınıf-küme doğasıyla ilgili sorunları önlemek için ivar'ı destekleyen bir NSArray nesnesi kullanan bir NSArray alt sınıfı oluşturdum. Bir nesnenin eklenmesini kabul etmek veya reddetmek için bloklar gerekir.
yalnızca NSString nesnelerine izin vermek için AddBlock
,
^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
}
Bir tanımlayabilirsiniz FailBlock
, filtreleme için incelikle başarısız başka diziye ekleyebilir veya - - bir eleman testinde başarısız olursa, ne yapacağına karar için bir istisna zam - Bu varsayılan değerdir.
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
Şunun gibi kullanın:
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);
Bu sadece örnek bir koddur ve gerçek dünya uygulamasında asla kullanılmamıştır. bunu yapmak için muhtemelen uygulanmış daha fazla NSArray yöntemine ihtiyaç vardır.
C ++ ve amaç-c'yi karıştırırsanız (yani mm dosya türünü kullanarak), çift veya tuple kullanarak yazmayı zorlayabilirsiniz. Örneğin, aşağıdaki yöntemde, std :: pair türünde bir C ++ nesnesi oluşturabilir, onu OC sarmalayıcı türünde bir nesneye dönüştürebilir (tanımlamanız gereken std :: pair sarmalayıcısı) ve sonra bunu bazılarına iletebilirsiniz. OC nesnesini kullanmak için tekrar C ++ nesnesine dönüştürmeniz gereken diğer OC yöntemi. OC yöntemi yalnızca OC sarıcı türünü kabul eder, böylece tür güvenliğini sağlar. Tip güvenliğini kolaylaştırmak için daha gelişmiş C ++ özelliklerinden yararlanmak için tuple, variadic şablon, tip listesi bile kullanabilirsiniz.
- (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];
}
2020, basit cevap. Sadece, türüne sahip değiştirilebilir bir diziye ihtiyacım olduğu oldu NSString
.
Sözdizimi:
Type<ArrayElementType *> *objectName;
Misal:
@property(nonatomic, strong) NSMutableArray<NSString *> *buttonInputCellValues;