Nastal čas na kakao - Jak správně importovat - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Seriály

Více seriálů



Informace

Nastal čas na kakao - Jak správně importovat

1. července 2004, 00.00 | Je zřejmé, že rozhraní třídy (@interface ... @end) je obvykle uloženo v hlavičkovém souboru, který vkládáme do zdrojových souborů, jež s třídou pracují, pomocí direktivy #import – obvykle to vypadá nějak takto...

Je zřejmé, že rozhraní třídy (@interface ... @end) je obvykle uloženo v hlavičkovém souboru, který vkládáme do zdrojových souborů, jež s třídou pracují, pomocí direktivy #import – obvykle to vypadá nějak takto:

Konkrétně, hlavičkový soubor obsahuje rozhraní třídy (a případné deklarace veřejných funkcí a konstant, jež s třídou spolupracují). Ten importuje samozřejmě standardní hlavičkové soubory Foundation.h (nebo Cocoa.h – v něm jsou navíc deklarace tříd a ostatních služeb pro grafické uživatelské rozhraní, s nimiž prozatím nepracujeme).

// Trida.h
#import <Cocoa/Cocoa.h> // standardní systémové deklarace
@interface Trida:NSObject
...
@end

Zdrojový soubor pak obsahuje implementaci třídy a samozřejmě importuje "vlastní" hlavičkový soubor; kromě toho může importovat hlavičkové soubory dalších tříd, jež využívá:

// soubor Trida.m
#import "Trida.h"
#import "Pomocnik.h"
@implementation Trida
...
-(void)foo {
  Pomocnik *p=[Pomocnik pomocnik];
  ...
}
...
@end

V takto jednoduchém případě samozřejmě k žádným problémům dojít nemůže – dokonce i v případě, že implementace třídy Pomocnik sama importuje naopak hlavičku Trida.h, bude vše v naprostém pořádku:

Mimochodem, pokud vám není jasné, proč se v direktivě #import někdy používají lomené závorky <...> a jindy uvozovky, podívejte se na popis direktivy #include v některé dobré knize o jazyku C. Velmi rámcově a ne zcela přesně, lomené závorky slouží pro import hlavičkových souborů, jež nejsou součástí projektu, zatímco uvozovky se používají pro import hlaviček z téhož projektu, v němž je soubor, který je obsahuje.

V čem je problém?

"Nebezpečné" může být importování dalších hlaviček do hlavičkového souboru – představme si následující situaci:

// soubor Trida.h -- špatně
#import <Cocoa/Cocoa.h>
#import "Pomocnik.h"
@interface Trida:NSObject
-(Pomocnik*)pomocnik;
...
@end
// soubor Pomocnik.h -- špatně
#import <Cocoa/Cocoa.h>
#import "Trida.h"
@interface Pomocnik:NSObject
-(Trida*)komuPomaham;
...
@end

Překlad takového projektu neproběhne dobře: sledujte se mnou – dejme tomu, že se nejprve začne překládat soubor Trida.h. Na jeho třetím řádku se direktivou #import vloží obsah souboru Pomocnik.h; nyní se tedy zpracovává ten. Na jeho (pomocníkově) třetím řádku je direktiva #import pro vložení obsahu souboru Trida.h – jak ovšem už dávno víme, direktiva #import je dostatečně chytrá na to, aby nevkládala znovu soubor, který již překladač zpracoval; protože soubor Trida.h se již překládá (tím jsme přece tuto úvahu začali), direktiva tedy neudělá nic. To by samo o sobě bylo v pořádku; problém ale nastane na pátém řádku pomocníka: zde používáme třídu Trida, ale o ní ještě překladač nic neví, neboť ve zpracování souboru Trida.h jsme se dosud nedostali dále, než ke třetímu řádku! Dojde tedy k chybě, protože identifikátor Trida je zatím neznámý.

V praxi samozřejmě málokdo napíše takto zřejmé "zacyklení"; pokud ale máme v projektu řadu tříd, jež vzájemně spolupracují, dosti snadno narazíme na "cyklus" o více třídách, kde hlavička A importuje B, hlavička B importuje C, a tak dále až po nějaké Z, jež importuje A – a problém je hotový.

Řešení

Řešením je (pokud možno, viz následující odstavec) nepoužívat v hlavičkových souborech jiné, než standardní importy; pokud potřebujeme informovat překladač o existenci jiné třídy, je vhodné namísto importu jejího hlavičkového souboru důsledně používat direktivu @class:

// soubor Trida.h -- správně
#import <Cocoa/Cocoa.h>
@class Pomocnik;
@interface Trida:NSObject
-(Pomocnik*)pomocnik;
...
@end

Teprve v souboru Trida.m obsahujícím implementaci použijeme direktivu #import pro vložení hlavičkového souboru Pomocnik.h: tam je to bezpečné, neboť implementace již se nikdy samy nikam nevkládají. Zároveň to stačí: hlavičkový soubor je třeba importovat pro faktické použití třídy, tedy předtím, než třídě (nebo její instanci) pošleme nějakou zprávu. To ovšem v hlavičkovém souboru nenastává; tam pouze deklarujeme proměnné či argumenty nebo návratové hodnoty, a pro takové služby zcela postačí direktiva @class.

Podobně v hlavičkovém souboru Pomocnik.h použijeme direktivu @class Trida, a soubor Trida.h vložíme až do zdrojového souboru Pomocnik.m.

Existují výjimečné případy, kdy bychom skutečně potřebovali importovat hlavičku třídy hned do hlavičkového souboru – to je tehdy, když např. hlavičkový soubor obsahuje definice inline funkcí, v nichž spolupracující třídě či jejím instancím posíláme zprávy, a podobně. Na takové situace je zapotřebí si dát zvláštní pozor; asi nejrozumnější řešení je postarat se o to, aby mezi importy takovýchto tříd nikdy nemohlo dojít k výše popsanému "zacyklení". Pokud to nejde, můžeme využít kategorií a protokolů pro oddělení vlastní deklarace třídy a některých jejích metod tak, abychom se "cyklů" zbavili.

Něco však i do hlaviček importovat musíme!

Z minulého odstavce nepřímo vyplývá, že vyjma importu standardních hlaviček ("v lomených závorkách") by se v hlavičkových souborech importovat nemělo. Jako obecné pravidlo je to sice pravda, ovšem vždy musíme hned do hlavičkového souboru importovat

  • hlavičkový soubor nadtřídy (pokud bychom tedy třídu Pomocnik deklarovali jako dědice třídy Trida, musíme direktivu #import "Trida.h" v hlavičkovém souboru Pomocnik.h použít;
  • hlavičkový soubor třídy, pro niž vytváříme kategorii.

To samozřejmě k výše popsaným problémům samo o sobě vést nemůže, neboť dědická hierarchie tříd a kategorií se nemůže "zacyklit" (je-li třída Pomocnik dědicem třídy Trida, nemůže být Trida dědicem Pomocnika ani některé z jeho podtříd).

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Začínáme s  

 

 

 

Nejčtenější články
Nejlépe hodnocené články
Apple kurzy

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: