ObjectiveC'deki değişken konumlarının açıklaması / tanımı?


113

İOS uygulamaları ve C hedefi üzerinde çalışmaya başladığımdan beri, değişkenlerin açıklanabileceği ve tanımlanabileceği farklı konumlar beni gerçekten şaşırttı. Bir yandan geleneksel C yaklaşımına sahibiz, diğer yandan bunun üzerine OO ekleyen yeni ObjectiveC direktiflerine sahibiz. Millet, değişkenlerim için bu konumları kullanmak istediğim en iyi uygulama ve durumları anlamama ve belki de mevcut anlayışımı düzeltmeme yardım edebilir misiniz?

Örnek bir sınıf (.h ve .m):

#import <Foundation/Foundation.h>

// 1) What do I declare here?

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

// 3) class-specific method / property declarations

@end

ve

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

@implementation SampleClass
{
    // 6) define ivars
}

// 7) define methods and synthesize properties from both public and private
//    interfaces

@end
  • Benim 1 ve 4'ü anladığım, bunların sınıf kavramını hiçbir şekilde anlamayan ve bu nedenle C'de tam olarak nasıl kullanılacağına dair C tarzı dosya tabanlı bildirimler ve tanımlar olduğudur. Onları gördüm. daha önce statik değişken tabanlı tekilleri uygulamak için kullanılır. Kaçırdığım başka kullanışlı kullanımlar var mı?
  • İOS ile çalışmaktan aldığım sonuç, ivarların @synthesize yönergesinin dışında neredeyse tamamen aşamalı hale getirildiği ve bu nedenle çoğunlukla göz ardı edilebilecekleri. Durum bu mu?
  • 5'e gelince: Neden özel arayüzlerde yöntem bildirmek isteyeyim? Özel sınıf yöntemlerim arayüzde bir bildirim olmadan gayet iyi derleniyor gibi görünüyor. Çoğunlukla okunabilirlik için mi?

Çok teşekkürler millet!

Yanıtlar:


154

Kafan karışıklığı anlayabiliyorum. Özellikle Xcode ve yeni LLVM derleyicisine yapılan son güncellemeler ivars ve özelliklerin bildirilme şeklini değiştirdiğinden.

"Modern" Objective-C'den önce ("eski" Obj-C 2.0'da) çok fazla seçeneğiniz yoktu. Başlıkta küme parantezleri arasında bildirilmek üzere kullanılan örnek değişkenleri { }:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

Bu değişkenlere yalnızca uygulamanızda erişebildiniz, diğer sınıflardan erişemiyordunuz. Bunu yapmak için, şuna benzer erişimci yöntemleri bildirmeniz gerekiyordu:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

Bu şekilde, mesaj göndermek için normal köşeli parantez sözdizimini kullanarak bu örnek değişkenini diğer sınıflardan da alıp ayarlayabildiniz (çağrı yöntemleri):

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

Her erişimci yöntemini manuel olarak bildirmek ve uygulamak oldukça can sıkıcı olduğundan @propertyve @synthesizeerişimci yöntemlerini otomatik olarak oluşturmak için tanıtıldı:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

Sonuç çok daha net ve daha kısa koddur. Erişimci yöntemleri sizin için uygulanacaktır ve daha önce olduğu gibi köşeli ayraç sözdizimini kullanmaya devam edebilirsiniz. Ancak, özelliklere erişmek için nokta sözdizimini de kullanabilirsiniz:

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Xcode 4.4'ten beri artık kendiniz bir örnek değişkeni bildirmek zorunda değilsiniz ve siz de atlayabilirsiniz @synthesize. Bir ivar bildirmezseniz, derleyici onu sizin için ekleyecek ve ayrıca kullanmanıza gerek kalmadan erişimci yöntemlerini üretecektir @synthesize.

Otomatik olarak oluşturulan ivar için varsayılan ad, alt çizgiyle başlayan ad veya mülkünüzdür. Oluşturulan ivar'ın adını kullanarak değiştirebilirsiniz.@synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

Bu tam olarak yukarıdaki kod gibi çalışacaktır. Uyumluluk nedenlerinden ötürü, başlıkta ivars bildirebilirsiniz. Ancak bunu yapmak istemenizin (ve bir özellik bildirmemenizin) tek nedeni özel bir değişken oluşturmak olduğundan, artık bunu uygulama dosyasında da yapabilirsiniz ve bu tercih edilen yoldur.

@interfaceUygulama dosyasındaki bir blok aslında bir Uzantıdır ve bildirim yöntemlerini iletmek (artık gerekli değildir) ve özellikleri (yeniden) bildirmek için kullanılabilir. Örneğin readonly, başlığınızda bir özellik bildirebilirsiniz.

@property (nonatomic, readonly) myReadOnlyVar;

ve bunu readwritesadece ivar'a doğrudan erişim yoluyla değil, özellik sözdizimini kullanarak ayarlayabilmek için uygulama dosyanızda yeniden beyan edin.

Tamamen dışındaki herhangi bir değişken bildirimi gelince @interfaceveya @implementationbloğun, evet o düz C değişkenlerinin ve çalışma tam olarak aynıdır.


2
mükemmel cevap! Ayrıca not: stackoverflow.com/questions/9859719/…
nycynik

44

Önce @ DrummerB'nin cevabını okuyun. Genelde yapmanız gerekenler ve ne yapmanız gerektiğine dair iyi bir genel bakış. Bunu akılda tutarak, özel sorularınıza:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

Buraya gerçek değişken tanımları girilmez (tam olarak ne yaptığınızı biliyorsanız, ancak bunu asla yapmıyorsanız, bunu yapmak teknik olarak yasaldır). Birkaç başka tür şey tanımlayabilirsiniz:

  • typdefs
  • enums
  • eksterna

Dışsallar değişken bildirimler gibi görünürler, ancak bunlar aslında onu başka bir yerde beyan etme vaadidir. ObjC'de, bunlar yalnızca sabitleri ve genellikle yalnızca dize sabitlerini bildirmek için kullanılmalıdır. Örneğin:

extern NSString * const MYSomethingHappenedNotification;

Daha sonra .mdosyanızda gerçek sabiti beyan edersiniz :

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

DrummerB'nin belirttiği gibi, bu mirastır. Buraya hiçbir şey koymayın.


// 3) class-specific method / property declarations

@end

Evet.


#import "SampleClass.h"

// 4) what goes here?

Yukarıda açıklandığı gibi harici sabitler. Ayrıca dosya statik değişkenleri buraya gidebilir. Bunlar, diğer dillerdeki sınıf değişkenlerinin eşdeğerleridir.


@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

Evet


@implementation SampleClass
{
    // 6) define ivars
}

Ama çok nadiren. Neredeyse her zaman clang (Xcode) 'un sizin için değişkenleri oluşturmasına izin vermelisiniz. İstisnalar genellikle ObjC olmayan ivarlar (Core Foundation nesneleri gibi ve özellikle bu bir ObjC ++ sınıfı ise C ++ nesneleri gibi) veya garip depolama semantiği olan ivarlar (herhangi bir nedenle bir özellik ile eşleşmeyen ivarlar gibi) civarındadır.


// 7) define methods and synthesize properties from both public and private
//    interfaces

Genel olarak artık @ sentezlememelisiniz. Clang (Xcode) bunu sizin için yapacak ve buna izin vermelisiniz.

Son birkaç yılda işler çarpıcı bir şekilde basitleşti. Yan etki, artık üç farklı dönem olmasıdır (Kırılgan ABI, Kırılgan Olmayan ABI, Kırılgan Olmayan ABI + otomatik sentezleme). Yani eski kodu gördüğünüzde biraz kafa karıştırıcı olabilir. Dolayısıyla basitlikten kaynaklanan kafa karışıklığı: D


Merak ediyorum, ama neden açıkça sentezlemeyelim? Bunu yapıyorum çünkü kodumun anlaşılmasını daha kolay buluyorum, özellikle de sentezlemeye alışkın olduğum için bazı özellikler sentezlenmiş erişimciler ve bazılarının özel uygulamaları olduğunda. Açık sentezin herhangi bir sakıncası var mı?
Metabble

Dokümantasyon olarak kullanmanın sorunu, gerçekten hiçbir şeyi dokümante etmemesidir. Synthesize kullanmanıza rağmen, erişimcilerden birini veya her ikisini geçersiz kılmış olabilirsiniz. Sentezleme satırından gerçekten yararlı bir şey söylemenin bir yolu yok. Hiçbir belgenin olmamasından daha kötü olan tek şey yanıltıcı belgelerdir. Dışarıda bırakın.
Rob Napier

3
6. numara neden nadirdir? Özel değişken almanın en kolay yolu bu değil mi?
pfrank

Özel mülk edinmenin en kolay ve en iyi yolu 5 numaradır.
Rob Napier

1
@RobNapier Bazen @ synthesize kullanmak gerekir (örneğin, bir özelliğin erişimcisi geçersiz kılınırsa)
Andy

6

Ben de oldukça yeniyim, bu yüzden umarım hiçbir şeyi mahvetmem.

1 & 4: C-tarzı global değişkenler: dosya geniş kapsamına sahiptirler. İkisi arasındaki fark, dosya genişliğinde olduklarından, ilki başlığı içe aktaran herkes tarafından kullanılabilirken, ikincisi değil.

2: örnek değişkenler. Çoğu örnek değişkeni, özellikler kullanılarak erişimciler aracılığıyla sentezlenir ve alınır / ayarlanır, çünkü bellek yönetimini güzel ve basit hale getirir ve ayrıca size kolay anlaşılır nokta gösterimi sağlar.

6: Uygulama ivarları biraz yenidir. Özel ivarlar koymak için iyi bir yer, çünkü yalnızca genel başlıkta gerekli olanı açığa çıkarmak istiyorsunuz, ancak alt sınıflar onları AFAIK miras almıyor.

3 & 7: Genel yöntem ve özellik bildirimleri, ardından uygulamalar.

5: Özel arayüz. Her şeyi temiz tutmak ve bir tür kara kutu efekti yaratmak için elimden geldiğince her zaman özel arayüzler kullanırım. Bilmeleri gerekmiyorsa, oraya koyun. Okunabilirlik için de yapıyorum, başka sebepler var mı bilmiyorum.


1
Hiçbir şeyi batırdığınızı düşünmeyin :) Birkaç yorum - # 1 & # 4 esp # 4 ile sık sık statik depolama değişkenleri görürsünüz. # 1 Genellikle harici depolamanın tanımlandığını ve ardından # 4'te ayrılan gerçek depolamayı görürsünüz. # 2) sadece bir alt sınıfın herhangi bir nedenle ona ihtiyacı varsa. # 5 artık özel yöntemleri bildirmek için gerekli değil.
Carl Veazey

Evet, ileriye dönük beyanı kendim kontrol ettim. Önceden bir özel yöntem, ileriye dönük bildirim olmadan kendisinden sonra tanımlanan diğerini çağırırsa uyarı verirdi, değil mi? Beni uyarmayınca biraz şaşırdım.
Metabble

Evet, derleyicinin yeni bir parçası. Son zamanlarda gerçekten çok ilerleme kaydettiler.
Carl Veazey

6

Bu, Objective-C'de açıklanan her tür değişkene bir örnektir. Değişken adı, erişimini gösterir.

Dosya: Animal.h

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

Dosya: Animal.m

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

İNotVisible değişkenlerinin diğer sınıflardan görünmediğini unutmayın. Bu yüzden onları ilan görünürlük konudur @propertyya @publicda değiştirmez.

Bir kurucu içinde, yan etkilerden kaçınmak için @propertybunun yerine alt çizgi kullanılarak bildirilen değişkenlere erişmek iyi bir uygulamadır self.

Değişkenlere erişmeye çalışalım.

Dosya: Cow.h

#import "Animal.h"
@interface Cow : Animal
@end

Dosya: Cow.m

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

Çalışma zamanını kullanarak görünmeyen değişkenlere hala erişebiliriz.

Dosya: Cow.m (bölüm 2)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

Görünmeyen değişkenlere erişmeye çalışalım.

Dosya: main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

Bu baskı

iMadeVisible 
iMadeVisible2 
iMadeVisible3

_iNotVisible2Alt sınıfa özel olan destek ivarına erişebildiğime dikkat edin . Amaç-C'de @private, istisnasız tüm değişkenler, işaretlenmiş olanlar bile okunabilir veya ayarlanabilir .

Farklı kuşlar oldukları için ilişkili nesneleri veya C değişkenlerini dahil etmedim. C değişkenlerine gelince, dışında tanımlanan herhangi bir değişken @interface X{}veya @implementation X{}dosya kapsamı ve statik depolamaya sahip bir C değişkenidir.

Bellek yönetimi özniteliklerini veya salt okunur / okuma yazma, alıcı / ayarlayıcı özniteliklerini tartışmadım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.