Waarom ondersteunt Objective-C geen privémethoden?

Ik heb een aantal strategieën gezien voor het declareren van semi-private methoden in Objective-C, maar er lijkt geen manier te zijn om een ​​echt private methode te maken. Dat accepteer ik. Maar waarom is dit zo? Elke uitleg die ik heb, zegt in wezen: “je kunt het niet, maar hier is een goede benadering.”

Er zijn een aantal trefwoorden toegepast op ivars(leden) die hun bereik bepalen, b.v. @private, @public, @protected. Waarom kan dit niet ook voor methoden? Het lijkt iets dat de runtime zou moeten kunnen ondersteunen. Is er een onderliggende filosofie die ik mis? Is dit opzettelijk?


Antwoord 1, autoriteit 100%

Het antwoord is… nou ja… simpel. Eenvoud en consistentie eigenlijk.

Objective-C is puur dynamisch op het moment van verzending van de methode. In het bijzonder gaat elke verzending van een methode door exact hetzelfde dynamische methode-oplossingspunt als elke andere verzending van een methode. Tijdens runtime heeft elke methode-implementatie exact dezelfde blootstelling en alle API’s die door de Objective-C-runtime worden geleverd en die werken met methoden en selectors werken op dezelfde manier voor alle methoden.

Zoals velen hebben geantwoord (zowel hier als in andere vragen), worden privémethoden tijdens het compileren ondersteund; als een klasse een methode niet declareert in zijn openbaar beschikbare interface, dan kan die methode net zo goed niet bestaan ​​voor zover het jouw code betreft. Met andere woorden, u kunt alle verschillende gewenste combinaties van zichtbaarheid bereiken tijdens de compilatie door uw project op de juiste manier te organiseren.

Het heeft weinig zin om dezelfde functionaliteit in de runtime te dupliceren. Het zou een enorme hoeveelheid complexiteit en overhead toevoegen. En zelfs met al die complexiteit, zou het nog steeds niet iedereen, behalve de meest informele ontwikkelaar, ervan weerhouden om je zogenaamd “privé”-methoden uit te voeren.

EDIT: Een van de aannames die ik heb
opgemerkt is dat privéberichten zouden
moet door de runtime gaan
resulterend in een potentieel grote
bovengronds. Is dit absoluut waar?

Ja, dat is zo. Er is geen reden om aan te nemen dat de uitvoerder van een klasse niet alle Objective-C-functies in de implementatie zou willen gebruiken, en dat betekent dat dynamische verzending moet plaatsvinden. Echteris er geen specifieke reden waarom privémethoden niet konden worden verzonden door een speciale variant van objc_msgSend(), aangezien de compiler zou weten dat ze privé waren; d.w.z. dit kan worden bereikt door een alleen-privémethodetabel toe te voegen aan de Class-structuur.

Er zou geen manier zijn voor een privé
methode om deze controle kort te sluiten of
de runtime overslaan?

Het kon de runtime niet overslaan, maar de runtime zou nietper se moeten controleren op privémethoden.

Dat gezegd hebbende, is er geen reden waarom een ​​derde partij niet opzettelijk objc_msgSendPrivate()zou kunnen aanroepen op een object, buiten de implementatie van dat object, en sommige dingen (KVO, bijvoorbeeld) zou dat moeten doen. In feite zou het gewoon een conventie zijn en in de praktijk niet veel beter dan de selectors van privémethoden vooraf te laten gaan of ze niet te vermelden in de interfacekoptekst.

Dit zou echter het pure dynamische karakter van de taal ondermijnen. Niet langer zou elke methode-verzending door een identiek verzendmechanisme gaan. In plaats daarvan zou je achterblijven in een situatie waarin de meeste methoden zich op één manier gedragen en een klein handjevol is gewoon anders.

Dit gaat verder dan de runtime, aangezien er veel mechanismen in Cocoa zijn gebouwd bovenop de consistente dynamiek van Objective-C. Zowel Key Value Coding als Key Value Observation zouden bijvoorbeeld zeer sterk moeten worden aangepast om privémethoden te ondersteunen – hoogstwaarschijnlijk door een exploiteerbare maas in de wet te creëren – of privémethoden zouden incompatibel zijn.


Antwoord 2, autoriteit 17%

De runtime zou dit kunnen ondersteunen, maar de kosten zouden enorm zijn. Elke selector die wordt verzonden, moet worden gecontroleerd of deze privé of openbaar is voor die klasse, of elke klasse moet twee afzonderlijke verzendtabellen beheren. Dit is niet hetzelfde, bijvoorbeeld variabelen, omdat dit beschermingsniveau wordt gedaan tijdens het compileren.

Ook zou de runtime moeten verifiëren dat de afzender van een privébericht van dezelfde klasse is als de ontvanger. U kunt ook privémethoden omzeilen; als de klasse instanceMethodForSelector:gebruikte, zou het de geretourneerde IMPaan een andere klasse kunnen geven zodat ze de private methode rechtstreeks kunnen aanroepen.

Privémethoden konden de berichtverzending niet omzeilen. Overweeg het volgende scenario:

  1. Een klasse AllPublicheeft een openbare instantiemethode doSomething

  2. Een andere klasse HasPrivateheeft een privé-instantiemethode, ook wel doSomething

  3. genoemd

  4. U maakt een array met een willekeurig aantal instanties van zowel AllPublicals HasPrivate

  5. Je hebt de volgende lus:

    for (id anObject in myArray)
        [anObject doSomething];
    

    Als u die lus zou uitvoeren vanuit AllPublic, zou de runtime u moeten stoppen om doSomethingte verzenden op de HasPrivate-instanties, maar deze lus zou bruikbaar zijn als het zich in de klasse HasPrivatebevond.


Antwoord 3, autoriteit 13%

De antwoorden die tot nu toe zijn gepost, geven een goed antwoord op de vraag vanuit een filosofisch perspectief, dus ik ga een meer pragmatische reden aanvoeren: wat zou er gewonnen worden door de semantiek van de taal te veranderen? Het is eenvoudig genoeg om privémethoden effectief te “verbergen”. Stel je bijvoorbeeld voor dat je een klasse hebt gedeclareerd in een headerbestand, zoals:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

Als u “private” methoden nodig heeft, kunt u deze ook in het implementatiebestand plaatsen:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end
@implementation MyObject
- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}
- (void) doSomeHelperThing
{
    // Do some helper stuff
}
@end

Natuurlijk, het is niet helemaalhetzelfde als C++/Java-privémethoden, maar het komt in feite dichtbij genoeg, dus waarom zou je de semantiek van de taal veranderen, evenals de compiler, runtime, enz., om een ​​functie toe te voegen die al op een acceptabele manier is geëmuleerd? Zoals opgemerkt in andere antwoorden, zou de semantiek voor het doorgeven van berichten — en hun afhankelijkheid van runtime-reflectie — het afhandelen van “privé”-berichten niet triviaal maken.


Antwoord 4, autoriteit 7%

De eenvoudigste oplossing is om enkele statische C-functies in uw Objective-C-klassen te declareren. Deze hebben alleen bestandsbereik volgens de C-regels voor het statische trefwoord en kunnen daarom alleen worden gebruikt door methoden in die klasse.

Geen gedoe.


Antwoord 5, autoriteit 6%

Ja, het kan worden gedaan zonder de runtime te beïnvloeden door gebruik te maken van een techniek die al door de compiler(s) wordt gebruikt voor het afhandelen van C++: name-mangling.

Het is niet gedaan omdat niet is vastgesteld dat het een aanzienlijk probleem in het codeerprobleem zou oplossen dat andere technieken (bijvoorbeeld voorvoegsel of onderstreping) voldoende kunnen omzeilen. IOW, je hebt meer pijn nodig om ingesleten gewoonten te overwinnen.

Je zou patches kunnen bijdragen aan clang of gcc die private methoden aan de syntaxis toevoegen en verminkte namen genereren die het alleen herkende tijdens het compileren (en prompt vergat). Dan zouden anderen in de Objective-C-gemeenschap kunnen bepalen of het echt de moeite waard was of niet. Op die manier gaat het waarschijnlijk sneller dan proberen de ontwikkelaars te overtuigen.


Antwoord 6, autoriteit 4%

In wezen heeft het te maken met Objective-C’s message-passing vorm van methode-aanroepen. Elk bericht kan naar elk object worden verzonden en het object kiest hoe het op het bericht reageert. Normaal gesproken zal het reageren door de methode uit te voeren die naar het bericht is genoemd, maar het kan ook op een aantal andere manieren reageren. Dit maakt privémethoden niet volledig onmogelijk – Ruby doet het met een soortgelijk systeem voor het doorgeven van berichten – maar het maakt ze wel wat onhandig.

Zelfs Ruby’s implementatie van privé-methoden is een beetje verwarrend voor mensen vanwege de vreemdheid (je kunt het object elk bericht sturen dat je wilt, behalve degenen op deze lijst!). In wezen zorgt Ruby ervoor dat het werkt door te verbieden dat privémethoden worden aangeroepen met een expliciete ontvanger. In Objective-C zou het nog meer werk vergen, aangezien Objective-C die optie niet heeft.


Antwoord 7

Het is een probleem met de runtime-omgeving van Objective-C. Terwijl C/C++wordt gecompileerd tot onleesbare machinecode , Objective-C heeft nog steeds enkele voor mensen leesbare kenmerken, zoals methodenamen als tekenreeksen. Dit geeft Objective-C de mogelijkheid om reflecterende-functies uit te voeren.

EDIT:Omdat het een reflectieve taal is zonder strikte privémethoden, maakt Objective-C meer “pythonisch” omdat je andere mensen vertrouwt die je code gebruiken in plaats van te beperken welke methoden ze kunnen noemen. Het gebruik van naamconventies zoals dubbele onderstrepingstekens is bedoeld om uw code te verbergen voor een gewone client-coder, maar zal niet voorkomen dat codeurs serieuzer werk moeten doen.


Antwoord 8

Er zijn twee antwoorden, afhankelijk van de interpretatie van de vraag.

De eerste is door de implementatie van de methode te verbergen voor de interface. Dit wordt meestal gebruikt met een categorie zonder naam (bijv. @interface Foo()). Hierdoor kan het object die berichten verzenden, maar andere niet – hoewel men nog steeds per ongeluk (of anderszins) kan negeren.

Het tweede antwoord, in de veronderstelling dat dit over prestaties en inlining gaat, is mogelijk gemaakt, maar in plaats daarvan als een lokale C-functie. Als u een ‘private foo(NSString *arg)’-methode wilt, doet u void MyClass_foo(MyClass *self, NSString *arg)en roept u het op als een C-functie zoals MyClass_foo(self,arg). De syntaxis is anders, maar werkt met de normale prestatiekenmerken van de privémethoden van C++.

Hoewel dit de vraag beantwoordt, moet ik erop wijzen dat de categorie zonder naam verreweg de meest gebruikelijke manier is om dit te doen met Objective-C.


Antwoord 9

Objective-C ondersteunt geen privémethoden omdat het deze niet nodig heeft.

In C++ moet elke methode zichtbaar zijn in de declaratie van de klasse. U kunt geen methoden hebben die iemand, inclusief het headerbestand, niet kan zien. Dus als je methoden wilt die code buiten je implementatie niet zou moeten gebruiken, heb je geen keus, de compiler moet je een hulpmiddel geven zodat je kunt vertellen dat de methode niet mag worden gebruikt, dat is het “private” sleutelwoord.

In Objective-C kunt u methoden hebben die niet in het headerbestand staan. U bereikt dus heel gemakkelijk hetzelfde doel door de methode niet aan het headerbestand toe te voegen. Er zijn geen privémethoden nodig. Objective-C heeft ook het voordeel dat je niet elke gebruiker van een klasse opnieuw hoeft te compileren omdat je privémethodes hebt gewijzigd.

Bijvoorbeeld variabelen, die je vroeger in het header-bestand moest declareren (niet meer), zijn @private, @public en @protected beschikbaar.


Antwoord 10

Een ontbrekend antwoord hier is: omdat privémethoden een slecht idee zijn vanuit het oogpunt van evolueerbaarheid. Het lijkt misschien een goed idee om een ​​methode privé te maken bij het schrijven, maar het is een vorm van vroege binding. De context kan veranderen en een latere gebruiker wil misschien een andere implementatie gebruiken. Een beetje provocerend: “Agile-ontwikkelaars gebruiken geen privémethoden”

In zekere zin is Objective-C, net als Smalltalk, bedoeld voor volwassen programmeurs. We vinden het belangrijk om te weten wat de oorspronkelijke ontwikkelaar dacht dat de interface zou moeten zijn, en nemen de verantwoordelijkheid om met de consequenties om te gaan als we de implementatie moeten veranderen. Dus ja, het is filosofie, geen implementatie.

Other episodes