Automatikus Referencia Számlálás

Eredeti Mike Ash, kifüggesztett- https://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html

Mivel a pillanatban, amikor az Apple bejelentette, olvasó kért meg, hogy írjak Automatikus Referencia Számlálás, vagy (Automatic Reference Counting – ARC). Ma van a napja. Beszélek arról, hogy az Apple új memória management system, hogyan működik, hogyan hozza ki a legtöbbet.

Konceptuális

Clang statikus elemző egy nagyon hasznos eszköz a megállapítás memória kezelése hibák kódot. Ha ön, mint én, már úgy nézett ki, a kimeneti elemző, gondoltam, “Ha a helyszínen a hiba, miért nem tudsz csinálni én is?”

Ez lényegében az, ami az ARC. A memória kezelése a szabályok sütött be a fordító, de ahelyett, hogy használja őket, hogy segítsenek a programozó találni hibákat, egyszerűen beszúrja a szükséges felhívja a saját.

ARC foglal egy középút között a garbage collection, manuális memória kezelése. Mint a szemetet, ARC szabadít a programozó írásban megtartja/release/autorelease hívások. Ellentétben a szemetet, azonban nem foglalkozik megtartja ciklus. Két objektum referenciák, hogy egymást soha nem lesz alapján gyűjtött ARC, még akkor is, ha semmi más nem utal őket. Mert, míg ARC szabadít a programozó foglalkozó legtöbb memória menedzsment problémák, a programozó még mindig van, hogy elkerülje vagy kézzel szünet ciklus referenciák a tárgy grafikon.

Amikor a végrehajtás részleteket, van még egy kulcs különbség ARC, az Apple végrehajtása a garbage collection: ARC, ez nem egy vagy-vagy szituáció. Az Apple szemétgyűjtő, vagy az egész program alatt fut GC, vagy egyik sem. Ez azt jelenti, hogy minden Objective-C kód alkalmazás, beleértve az összes Apple keretek, mind a harmadik fél könyvtárak lehet tartalmaznia kell GC kompatibilis az ön számára, hogy kihasználják a GC. Ezzel szemben az ARC azt a bizottságnak békésen a nem ARC kézi memória sikerült kód ugyanezt a kérelmet. Ez lehetővé teszi, hogy megtérít projektek darabonként anélkül, hogy a hatalmas kompatibilitás, megbízhatósági problémák, hogy a garbage collection összefutottunk, amikor először bevezetésre.

Xcode

ARC áll rendelkezésre Xcode 4.2, jelenleg-ban béta, csak összeállításakor a Clang (egy “Az Apple LLVM fordító”). A beállítás neve, nyilván elég”, Objective-C, Automatikus Referencia Számlálás”. Kapcsolja be, illetve ki.

Ha dolgozik a meglévő kódot, a beállítás módosítása termel hatalmas mennyiségű hibát. ARC nem csak kezeli a memóriát, de megtiltja, hogy megpróbál csinálni magadnak. Illegális kézzel küldeni megtartja/release/autorelease használata ARC. Mivel normális, nem ARC kód tele van ez a cucc, akkor kap egy csomó hibát.

Szerencsére Xcode kínál egy eszköz átalakítani a meglévő kódot. Válassza a Szerkesztés -> Refactor… -> Convert hogy Objective-C ARC es Xcode végigvezeti a konvertáló kódot. Bár lehet, hogy bizonyos helyzetekben, ahol segítségre van szüksége, hogy kitaláljuk, mi lesz, kell a folyamat nagyrészt automatikus.

Alapvető Funkciók

Cocoa memória gazdálkodás szabályai meglehetősen egyszerű. Röviden:

  1. Ha alloc, új, másolása, vagy fenntartani egy tárgy, egyensúlyban kell lennie, hogy a kiadás, vagy autorelease.
  2. Ha megszerezni egy tárgyat, valami más, mint a fenti, de szükség van rá, hogy életben maradj, hosszú távú, biztos eltulajdonítani. Ez természetesen kell, hogy legyen kiegyensúlyozott később.

Ezek igen alkalmas az automatizálásra. Ha ezt írom:

    Foo *foo = [[Foo alloc] init];
    [foo something];
    return;

A fordító látom a kiegyensúlyozatlan alloc. A kód így átalakult:

    Foo *foo = [[Foo alloc] init];
    [foo something];
    [foo release];
    return;

A valóság az, hogy a fordító nem helyezze be az üzenetet küldeni, hogy engedje el. Ehelyett beszúr egy hívást, hogy egy különleges runtime funkció:

    Foo *foo = [[Foo alloc] init];
    [foo something];
    objc_release(foo);
    return;

Ez lehetővé teszi, hogy egy optimalizálás. A közös esetben, ha a kiadás nem felülírva, a objc_release funkció bypass Objective-C üzenetek, ami egy kicsit sebesség szert.

Az automatika lehet, hogy a kód biztonságosabb. A legtöbb Cocoa programozók értelmezni a “hosszú távú” a szabály #2 azt jelenti, tárgyak, tárolt például változók vagy hasonló helyeken. Általában nem szoktunk tartani, illetve kiadás helyi ideiglenes objektumok:

    Foo *foo = [self foo];
    [foo bar];
    [foo baz];
    [foo quux];

Ez azonban veszélyes lehet:

    Foo *foo = [self foo];
    [foo bar];
    [foo baz];
    [self setFoo: newFoo];
    [foo quux]; // crash

A szokásos módon, a munka körül, hogy a -foo getter egy retain/ autorelease, mielőtt visszatért az érték. Ez működik, de lehet építeni egy csomó átmeneti tárgyak vezető túlzott memória használat. ARC azonban az lesz, paranoiás, majd helyezze extra hívásokat itt:

    Foo *foo = objc_retainAutoreleasedReturnValue([self foo]);
    [foo bar];
    [foo baz];
    [self setFoo: newFoo];
    [foo quux]; // fine
    objc_release(foo);

Hasonlóképpen, még akkor is, ha írsz egy sima vagány, ARC lesz hogy biztonságos:

    - (Foo *)foo
    {
        return objc_retainAutoreleaseReturnValue(_foo);
    }

De várjunk csak, ez nem oldja meg a problémát a túlzott ideiglenes objektumok! Még most is megtartja/autorelease sorrend a getter, megtartja/release kombináció a hívó kód. Ez lényegesen kevésbé hatékony!

Nem kell aggódni. Mint azt már említettük, ARC bocsát ki, ezek a speciális hívások helyett egyszerű üzenetet küld alkalmazásában optimalizálás. Amellett, hogy egyszerűen, hogy megtartja, majd engedje el gyorsabban, ezek a hívások képes arra, hogy megszüntesse az egyes műveletek összesen.

Amikor objc_retainAutoreleaseReturnValue fut, úgy néz ki, a halmot, majd megragadja a visszatérési címet a hívó. Ez lehetővé teszi, hogy pontosan mi fog történni, miután befejezi. Amikor a fordító optimalizálás bekapcsolva, a hívás objc_retainAutoreleaseReturnValue lesz figyelemmel követni-hívja optimalizálás, illetve a visszatérési cím a lényeg, hogy a hívás objc_retainAutoreleasedReturnValue.

Ezzel az őrült vissza-cím vizsgálata, a runtime képes látni, hogy szól, hogy végre egy kis felesleges munka. Ezért kiküszöböli a autorelease, állítja egy a zászló, mely azt mondja, hogy a hívó, hogy megszüntesse a megőrzése. Az egész sorozat végén egy egységes megtartja a getter, egyetlen kiadás a hívó kód, amely egyrészt teljesen biztonságos, hatékony.

Vegye figyelembe, hogy ez optimalizálás teljes mértékben kompatibilis a nem-ARC kód. Abban az esetben, ha a getter nem használja ARC, a zászló, nem kell beállítani, hogy a hívó végez egy teljes megtartja/release kombináció. Abban az esetben, ha a getter használ ARC de a hívó fél nem tesz, a mit akar látni fogja, hogy nem válaszol a kódot, hogy azonnal hívja a különleges runtime funkció, valamint elvégzi a teljes retain/autorelease kombináció. Néhány hatékonyság elveszett, de a korrektség megmarad.

Amellett, hogy ez az egész, ARC is automatikusan hoz létre, vagy kitölt egy -dealloc módszer minden osztályban, hogy kiadja az esetben változók. Ez még mindig lehetséges, hogy kézzel végrehajtani -dealloc, szükséges osztályok, amelyek kezelése külső források, de már nincs szükség (vagy lehetséges), manuális kioldó például változók. ARC lesz még a [super dealloc] a vég, ilyen nem kell. Korábban, lehet, hogy írtam ezt:

    - (void)dealloc
    {
        [ivar1 release];
        [ivar2 release];
        free(buffer);

        [super dealloc];
    }

Most már csak írd ezt:

    - (void)dealloc
    {
        free(buffer);
    }

Abban az esetben, ha a dealloc módszer csak kibocsátások -dealloc változók, egyszerűen lehet teljesen kiküszöbölni.

Ciklusokat, illetve a Gyenge Referenciák

ARC még mindig megköveteli, hogy a programozó, hogy kézzel oldja referencia ciklus, a legjobb módja annak, hogy megoldja referencia ciklus általában használni gyenge referenciák.

ARC nyújt nullázás gyenge referenciák. Ezek a gyenge referenciák amely nem csak, hogy nem tartják meg a hivatkozott objektum életben, de ami szintén automatikusan lesz nulla, ha a hivatkozott objektum megsemmisül. Nullázás gyenge referenciák elkerüljék a lehetséges lógó mutatók, valamint a kapcsolódó összeomlik, titokzatos viselkedés.

Ahhoz, hogy egy nullázás gyenge változó, egyszerűen előtag a nyilatkozatot __weak. Például, itt van egy gyenge, például változó:

    @interface Foo : NSObject
    {
        __weak Bar *_weakBar;
    }

Hasonlóképpen a helyi változók:

  __weak Foo *_weakFoo = [object foo];

Ezután használhatja, mint bármely más változót, melynek értéke automatikusan egyre nil ha megfelelő:

   [_weakBar doSomethingIfStillAlive];

Ne feledje azonban, hogy egy __weak változó válhat nil szinte bármikor. Memória kezelése egy eredendően több menetes tevékenység, egy gyengén hivatkozott objektum lehet semmisíteni az egyik szál, míg a másik szál fér hozzá. Ilyen kódot ezért nem érvényes:

    if(_weakBar)
        [self mustNotBeNil: _weakBar];

Ehelyett tárolja a tárgy, egy helyi erős referencia teszt:

    Bar *bar = _weakBar;
    if(bar)
        [self mustNotBeNil: bar];

Mert bar egy erős utalás arra, hogy a tárgy garantált, hogy életben maradjon (a változó nem-nil) a kódexben.

ARC végrehajtása nullázás gyenge referenciák igényel közel közötti koordináció az Objective-C referencia számlálás rendszer, valamint a nullázás gyenge referencia-rendszer. Ez azt jelenti, hogy minden osztály, amely felülírja tartani, illetve a kiadás nem lehet a cél egy nullázás gyenge referencia. Bár ez nem gyakori, egy kis Cocoa osztályok, mint NSWindow, szenved ez a korlátozás. Szerencsére, ha bejön egy ilyen esetekben, akkor tudni fogja, hogy azonnal, mivel a program összeomlik, egy üzenet, mint ez:

    objc[2478]: cannot form weak reference to instance (0x10360f000) of class NSWindow

Ha nagyon kell, hogy egy gyenge hivatkozás osztályok, mint ezek, akkor használja a __unsafe_unretained selejtező helyett __weak.. Ez létrehoz egy gyenge referencia, amely nem nullázás. Biztosítani kell, hogy te soha ne használjon ilyen mutató (lehetőleg nullázás ki kézzel) után az objektum a pontokat, hogy megsemmisült. Légy óvatos, mint a nem-nullázás gyenge referenciák a tűzzel játszik.

Amíg lehet építeni programok használata ARC, hogy fut a Mac OS X 10.6, az iOS 4, a nullázás gyenge referenciák nem állnak rendelkezésre azok az operációs rendszer. Minden gyenge hivatkozások __unsafe_unretained itt. Mert nem nullázás gyenge referenciák olyan veszélyes, ez a korlátozás jelentősen csökken a vonzó ARC azokat az operációs rendszer véleményem szerint.

Tulajdonságok

Mivel a tulajdonságok olyan szorosan csatolt memória kezelése, érthető, hogy az ARC lenne bevezetni egy új viselkedés.

ARC vezet be néhány új tulajdonosi módosítók. Kijelentette, ingatlan, mint erős teszi, hogy az ingatlan egy erős utalás. Kijelentette, ez olyan gyenge használ nullázás gyenge referencia. A unsafe_unretained módosító használ, nem nullázás gyenge referencia. Mikor @szintetizálni használt, a fordító teremt például a változó azonos tárolási fajta.

A meglévő módosító hozzárendelése, másolása, illetve megtartani továbbra is ugyanúgy működik, ahogy korábban is tette. Nevezetesen, rendeljen létrehoz egy nem-nullázás gyenge referencia, tehát lehetőség szerint kerülni kell.

Eltekintve az új módosítók, tulajdonságok dolgoznia, mint mindig.

Tömb

Tömb Objective-C tárgyakat mint ilyen, szintén által kezelt ARC. Tömb speciális memória gazdálkodási követelményeket, ARC kezeli őket ennek megfelelően. Tömb közvetlen használata erősen kerülendő kell másolni, sem megőrizni, ami a gyakorlatban azt jelenti, hogy az a legjobb, hogy másolás helyett megtartja a blokkok mindenhol. ARC követi ezt a gyakorlatot.

Továbbá, ARC tudja, hogy a blokk közvetlen használata erősen kerülendő kell másolni, ha használják, miután a jelenlegi hatálya visszatér. Nem ARC kód van szüksége, hogy kifejezetten másold autorelease vissza blocks:

    return [[^{
        DoSomethingMagical();
    } copy] autorelease];

ARC ez egyszerűen válik:

    return ^{ DoSomethingMagical(); };

Azonban vigyázz! ARC jelenleg nem jelenti automatikusan másolatot egy blokk szó, hogy át egy id. Szóval, amíg ez a kód:

    dispatch_block_t function(void)
    {
        return ^{ DoSomethingMagical(); };
    }

Ez a kód nem:

    id function(void)
    {
        return ^{ DoSomethingMagical(); };
    }

Könnyű munka körül egyszerűen csak be kell másolni a blokk, de ez is valami, hogy legyen óvatos:

    return [^{ DoSomethingMagical(); } copy];

Hasonlóképpen kell külön másolja a blokkokat, hogy néz ki, mint egy id paraméterek:

    [myArray addObject: [^{ DoSomethingMagical(); } copy]];

Szerencsére, úgy tűnik, hogy ez csak egy él, ha a süllyesztőben, s valószínűleg hamarosan kijavítják. Nincs probléma dobott egy extra kézi másolás ha nem vagy biztos benne.

Egy másik jelentős változás ARC ez a viselkedés __block képzett változók. A __block azonosítója lehetővé teszi, hogy egy blokk módosítása a rögzített változó:

    id x;
    __block id y;
    void (^block)(void) = ^{
        x = [NSString string]; // error
        y = [NSString string]; // works
    };

Anélkül, ARC __block is a mellékhatása nem megőrzi a tartalmát, amikor elkapta egy blokk. Blokk automatikusan megtartja, majd engedje el minden tárgyat mutatók elfog, de __block mutatók különleges borított, jár, mint egy gyenge mutató. Ez lesz egy közös minta támaszkodni ez a viselkedés segítségével __block elkerülésére megtartja ciklus.

Alatt ARC, __block k most is megőrzi a tartalmát, csak úgy, mint a többi rögzített tárgy mutatók. A kódot hogy használja __block elkerülésére megtartja a ciklus többé már nem működik. Ehelyett használj __weak, mint a fent leírt.

Díjmentes Áthidaló

ARC csak akkor működik, Objective-C típusok. CoreFoundation típusok még nem sikerült kézzel a programozó. Mert tanmese arról, tulajdonosi, ARC tiltja standard vet között mutatók Objective-C tárgyak, mutatók más típusú, beleértve a tanácsot, hogy CoreFoundation tárgyakat. A következő kódot, mely meglehetősen tipikus alatt kézi memória kezelése, nem gyűjt ARC:

    id obj = (id)CFDictionaryGetValue(cfDict, key);

Annak érdekében, hogy ez össze újra, meg kell mondani ARC a tulajdonosi szemantika benne segítségével különleges casting kommentárok. Ezek a kommentárok vannak ___bridge, __bridge_retained, __bridge_transfer.

A legegyszerűbb megérteni __bridge. Ez egy közvetlen konverzió, nem tulajdonosi következményei. ARC kap értéket, akkor sikerül általában. Ez az, amit akarunk, a fenti:

    id obj = (__bridge id)CFDictionaryGetValue(cfDict, key);

A másik szereposztás kommentárok a tulajdonjogot, illetve az ARC rendszer. Ezek segíthetnek enyhíteni a fájdalmat, amikor áthidaló oda-vissza.

Itt egy példa segítségével áthidaló egy olyan helyzetben, ahol a visszaadott objektum van szüksége, hogy fel kell szabadítani.

    NSString *value = (NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    [self useValue: value];
    [value release];

Ha ezt az ARC segítségével __bridge eltávolítása a kiadás, különben nem változik, akkor a végén egy sárgát:

    NSString *value = (__bridge NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    [self useValue: value];

Copy be ezt a kódot kell vetni a kiadás. ARC ad ki egy retain ha bekapcsolom az value, akkor az egyensúlyt, hogy a release, ha az valuek nincs használatban. Mivel semmi egyenlegek az eredeti Copy, hogy az objektum kiszivárgott.

Tudjuk kerülni egy kis extra kód:

    CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    NSString *value = (__bridge NSString *)valueCF;
    CFRelease(valueCF);

    [self useValue: value];

Ez már meglehetősen bőbeszédű. Mivel pont az a lényege, díjmentes áthidaló, hogy minél kevésbé fájjon, pont az a lényege, ARC hogy távolítsa el a kell írni memória kezelése kód, jó lenne, ha ezt lehet, hogy több egyszerű.

A __bridge_transfer jegyzet megoldja ezt a problémát. Ahelyett, hogy egyszerűen mozgassa a mutatót az érték ARC, mozog az érték, illetve tulajdonjogát másra átruházza. Amikor __bridge_transfer használt öntött, azt mondja, ARC, hogy ez a tárgy már megtartották, de az ARC nem kell tartani újra. Mivel ARC kerül a tulajdonjog, még a kiadás, ha vége. A nettó eredmény az, hogy minden működik úgy, ahogy kellene:

    NSString *value = (__bridge_transfer NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
    [self useValue: value];

Díjmentes áthidaló mindkét irányban működik. Mint korábban, ARC nem teszi lehetővé egy standard leadott átalakítani egy Objective-C objektum mutató egy CoreFoundation tárgy mutató. Ez a kód nem összeállítása alapján ARC:

    CFStringRef value = (CFStringRef)[self someString];
    UseCFStringValue(value);

Hozzátéve __bridge a leadott teszi, hogy lefordítani, de a kapott kódot veszélyes:

    CFStringRef value = (__bridge CFStringRef)[self someString];
    UseCFStringValue(value);

Mivel az ARC nem sikerül az élettartama value, kiengedi tulajdonjogát az objektum azonnal, mielőtt meghalt, hogy UseCFStringValue, potenciálisan okozó baleset, vagy más viselkedést. Segítségével __bridge_retained, meg tudjuk mondani, ARC, hogy a tulajdonjogot a rendszer a kezünkbe. Mivel a tulajdonjog átruházásra kerül, azonnal felelős elengedi a tárgy, ha végzett vele, mint bármely más CF kód:

    CFStringRef value = (__bridge_retained CFStringRef)[self someString];
    UseCFStringValue(value);
    CFRelease(value);

Ezek a leadott kommentárok hasznos kívül díjmentes áthidaló is. Minden alkalommal meg kell tárolni egy tárgy mutatót tároló, ami nem sikerült, mint az Objective-C objektum, sima az út. Vannak void * összefüggésben mutatók található különböző helyeken a Cocoa, valamint egy kiemelkedő példa lap. Nélkül ARC:

    NSDictionary *contextDict = [NSDictionary dictionary...];
    [NSApp beginSheet: sheetWindow
       modalForWindow: mainWindow
        modalDelegate: self
       didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
          contextInfo: [contextDict retain]];


    - (void)sheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)code contextInfo: (void *)contextInfo
    {
        NSDictionary *contextDict = [(id)contextInfo autorelease];
        if(code == NSRunStoppedResponse)
            ...
    }

Mint korábban, ez alá esik ARC, mert a normális vet között tárgy, nem tárgy mutatók nem engedélyezett. Használatával azonban a leadott módosítók, tudjuk, hogy nem csak az ARC, hogy megengedi, hanem kap ARC, hogy a szükséges memória menedzsment minket:

    NSDictionary *contextDict = [NSDictionary dictionary...];
    [NSApp beginSheet: sheetWindow
       modalForWindow: mainWindow
        modalDelegate: self
       didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
          contextInfo: (__bridge_retained void *)contextDict];


    - (void)sheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)code contextInfo: (void *)contextInfo
    {
        NSDictionary *contextDict = (__bridge_transfer NSDictionary *)contextInfo;
        if(code == NSRunStoppedResponse)
            ...
    }

Összefoglalva:

  • __bridge egyszerűen transzferek egy mutató között ARC, illetve a nem-ARC, nem a tulajdonjog átruházása.
  • __bridge_transfer mozog, nem Objective-C mutatót objective-C is tulajdonjogát másra átruházza, mint az ARC kiadja az érték.
  • __bridge_retained mozog egy Objective-C mutatót nem Objective-C mutató, valamint tulajdonjogát másra átruházza, például, hogy a programozó, vagy a felelős később hív CFRelease, vagy más módon felszabadító tulajdonjogát a tárgy.

Struktúrák

Alatt ARC, struktúrák, Objective-C objektum mutatók nagyjából ne keverjük össze. A probléma az, hogy nincs jó módszer, az a fordító, hogy tudjam, mikor egy adott struktúra megsemmisült vagy másolt, s így nem jó hely a fordító, hogy helyezze be a szükséges megőrizni, majd engedje fel a hívásokat. Mert ez egy ilyen nehéz probléma, mert üzembe tárgy mutatók struktúrák annyira szokatlan, hogy mindegy, ARC, csak ad a megoldáshoz. Ha azt szeretné, hogy az Objective-C objektum mutatót egy struct, neked kell felelned a __unsafe_unretained, kezelni az összes problémát, valamint a veszély, hogy ez azt jelenti.

Mert nagyon ritka, hogy tegye Objective-C mutatók a struktúrákat, valószínű, hogy ez nem lesz probléma a kódot. Ha igen, a legjobb, ha a változás a struct-ba egy könnyű Objective-C osztály helyett. ARC indul a memória kezelése a probléma elmúlik.

Tovább Olvasni

Miközben az Apple hivatalos dokumentációt ARC még mindig titokban, míg Xcode 4.2 béta, nagy információt a rendszer elérhető a Clang oldalon itt: http://clang.llvm.org/docs/AutomaticReferenceCounting.html

Következtetés

Automatikus referencia számlálás jelentősen csökkenti a terheket a programozó foglalkozó memória kezelése. ARC ez nem egy teljes szemétgyűjtő. Nem érzékel megtartja ciklus, ezeket kell foglalkozni, törött, amelyet a programozó. Még mindig sok a piszkos munkát írni Kakaó kódot, majd a nullázás gyenge referenciák, hogy rendelkezik egy hatékony eszköz foglalkozik ciklus.

A dolgok trükkösebb, amikor CoreFoundation tárgyak, illetve a díjmentes áthidaló. ARC korlátozza magát, hogy foglalkozik, Objective-C, így a programozó mindig kell kezelni a CoreFoundation oldalon kézzel. Amikor konvertáló között, Objective-C, CoreFoundation mutató, a különleges __bridge leadott módosító kell tájékoztatni ARC a memória kezelése a szemantika a konverzió.

 

 

Vissza a főoldalra

Leave a Reply

Your email address will not be published. Required fields are marked *