Hat jemand ein bvernünftiges foreach-makro?

  • Original von timnic
    Ein Makro, das ich recht häufig benutze, habe ich von Allan Odgaard:

    Quellcode

    1. #ifndef forall
    2. #define forall(container,var) \
    3. for(id _enumerator = [container objectEnumerator], var; \
    4. var = [_enumerator nextObject]; )
    5. #endif

    Für mich funktioniert das recht ordentlich. Das einzige Problem ist, dass man manchmal noch var in den richtigen Typ "casten" muss.


    Das kann man z.B. auch nur einmal/Scope nutzen.
    "Wales is the land of my fathers. And my fathers can have it." - Dylan Thomas
  • Original von -Nuke-
    Original von gritsch
    ich weis net woher es ist aber es gibt so ein makro das auch um einiges schneller ist als ein enumerator. natürlich nur bei vielen elementen. Wird eben realisiert durch das Holen des funktionspointers und dem Umgehen des dynamischen Auffindens der funktion durch obj-C


    Hatten wir im Forum hier doch schon mal getestet (for vs. enumerator vs. IMP). Brachte doch nur was bei >50000 Elementen, soweit ich das in Erinnerung hatte. Oder trübt mich da jetzt was? ;)


    da kann ich aber nicht zustimmen. ich weis es aus eigener erfahrung.
    Was zwar jetzt hier net hergehört aber trotzdem interessant zu wissen ist, dass wenn man zb viele arrays hat mit jeweils nur wenigen elementen und diese alle durchgehen muss dann sollte man eine for-schleife verwenden da diese um einiges schneller ist als den enemuerator zu verwenden ;)
  • Original von Tom9811
    Eine for-Schleife? Du meinst mit objectAtIndex:? (Das hatten wiir probiert.)

    Bisher ging es doch um IMP vs. RT-Dispatching. IMP kannst du auch bei einer while-Schleife verwenden.


    nein, ich meine for und enumerator. denn [collection objectEnumerator] dauert um einiges länger also [collection count].
  • Original von Tom9811
    Ja, aber objectAtIndex: dauert einiges länger als nextObject. Das hatten wir in der Tat mal probiert. Schau mal nach.


    aber

    unsigned i, count = [collection count]
    count mal [collection objectAtIndex:i] wobei count sehr klein

    ist schneller als

    NSEnumerator *enumerator = [collection objectEnumerator];
    X mal [enumerator nextObject] wobei X gleich count vom letzten codeschnipsel ist und daher sehr klein ist (bereich 0 bis 3, wobei ich nicht getestet habe wie es zb bei 10 ist...)
  • Klar, in der konkreten Implementierung kann es sein, dass die Anzahl der Objekte im Array so klein ist, dass die reine Erzeugung des Enumerators mehr zu Buche schlägt. Nur ist ist dann in der regel so, dass das Laufzeitverhalten ohnehin keine Rolle spielt.

    Wenn du viele kleine Arrays hast (wie du schriebst), dann kann das passieren. Allerdings wirst du dann das Array der Arrays auch wieder mit einem Enumerator scannen wollen. Denn hier hast du ja eine große Anzahl an Objekten im Array-Array.

    Bei einer Lösung mittels Enumerator hast du zusätzlich den Vorteil der Flexibilität. Du kannst die Collection ändern. Und dies ist nicht ganz unwichtig, wenn du etwa auf CoreData umstellen willst. (Mal abgesehen davon, dass man viel zu selten NSSet anstelle von NSArray verwendet. Das sind wohl alte C-Gewohnheiten.)

    Bei beiden Lösungen ist es freilich möglich und anzudenken, IMP zu verwenden. Das erspart die halt das Runtime-Dispatching.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Tom9811
    Klar, in der konkreten Implementierung kann es sein, dass die Anzahl der Objekte im Array so klein ist, dass die reine Erzeugung des Enumerators mehr zu Buche schlägt. Nur ist ist dann in der regel so, dass das Laufzeitverhalten ohnehin keine Rolle spielt.


    oh doch, hat es. sosnt hätte ichs hier nicht erwähnt

    Original von Tom9811
    Wenn du viele kleine Arrays hast (wie du schriebst), dann kann das passieren. Allerdings wirst du dann das Array der Arrays auch wieder mit einem Enumerator scannen wollen. Denn hier hast du ja eine große Anzahl an Objekten im Array-Array.


    Ja, den container der kleinen arrays gehe ich mit einem enumerator durch.

    Original von Tom9811
    Bei einer Lösung mittels Enumerator hast du zusätzlich den Vorteil der Flexibilität. Du kannst die Collection ändern. Und dies ist nicht ganz unwichtig, wenn du etwa auf CoreData umstellen willst. (Mal abgesehen davon, dass man viel zu selten NSSet anstelle von NSArray verwendet. Das sind wohl alte C-Gewohnheiten.)


    beim enumerator kann ich aber zb keine objekte aus der collection entfernen. Auch nen backwardIterator kann man mit ner for realisieren ;) Keine angst, die einzelnen kleinen Arrays liegen in einem set weil das einfach um einiges schneller ist ;-). Und was ein Enumerator mit CoreData zu tun haben soll verstehe ich jetzt auch nicht wirklich... Außerdem verwende ich SQLite direkt weil ich da einfach viel schneller an meine daten rankomme als mit CoreData ;)

    Original von Tom9811
    Bei beiden Lösungen ist es freilich möglich und anzudenken, IMP zu verwenden. Das erspart die halt das Runtime-Dispatching.


    Ja, aber wie du sagst kann man das bei beiden beispielen machen. warum sollte ich die aber verkomplizieren hier?
  • beim enumerator kann ich aber zb keine objekte aus der collection entfernen.
    Ach und das geht mit einerm for mit nur einem -count(NSArray)? Na gut, wenn du eine reverse Iteration hast und nur hinten löschst. Das war es aber auch schon. Mutmaßlich funktioniert das auch mit einem Enumerator. Ich würde es allerdings nicht darauf ankommen lassen. :)

    Auch nen backwardIterator kann man mit ner for realisieren ;)

    Das geht auch mit Enumerator: -reverseObjectEnumerator(NSArray)

    Keine angst, die einzelnen kleinen Arrays liegen in einem set weil das einfach um einiges schneller ist ;-).

    Na, Angst hatte ich eigentlich nicht.
    Und was ein Enumerator mit CoreData zu tun haben soll verstehe ich jetzt auch nicht wirklich...

    Du bekommst bei einer To-Many-Relationship ein NSSet zurück. Wenn du bisher aus Gewohnheit mit NSArray gearbeitet hast, um da eine Beziehung zu scannen, wirst du jetzt deinen Code etwas umschreiben müssen. -objectAtIndex:(NSSet) gibt es nämlich nicht.

    Außerdem verwende ich SQLite direkt weil ich da einfach viel schneller an meine daten rankomme als mit CoreData ;)

    *PLONK* ;)
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Tom9811
    beim enumerator kann ich aber zb keine objekte aus der collection entfernen.
    Ach und das geht mit einerm for mit nur einem -count(NSArray)? Na gut, wenn du eine reverse Iteration hast und nur hinten löschst. Das war es aber auch schon. Mutmaßlich funktioniert das auch mit einem Enumerator. Ich würde es allerdings nicht darauf ankommen lassen. :)


    bitte? wie löscht du denn ein objekt aus einem array raus das du grad durchiterierst? mit ner vor geht das sehr vol. removeObjectAtIndex:i; i--; count--; Beim hinzufügen von objekten natürlich auch. mit nem enumerator wirst dir da schwer tun...

    Original von Tom9811
    Auch nen backwardIterator kann man mit ner for realisieren ;)

    Das geht auch mit Enumerator: -reverseObjectEnumerator(NSArray)


    den meinte ich ja und ich hab nur gesagt dass das auch mit ner for zu realisieren ist und keine sache die nur mit einem enumerator funktioniert.

    Original von Tom9811
    Und was ein Enumerator mit CoreData zu tun haben soll verstehe ich jetzt auch nicht wirklich...

    Du bekommst bei einer To-Many-Relationship ein NSSet zurück. Wenn du bisher aus Gewohnheit mit NSArray gearbeitet hast, um da eine Beziehung zu scannen, wirst du jetzt deinen Code etwas umschreiben müssen. -objectAtIndex:(NSSet) gibt es nämlich nicht.


    ich kenn die unterschiede der einzelnen collections ;)

    Original von Tom9811
    Außerdem verwende ich SQLite direkt weil ich da einfach viel schneller an meine daten rankomme als mit CoreData ;)

    *PLONK* ;)
    [/quote]

    ich finde es eben besser dass ein programm in etwa 4 sekunden hochstartet oder in etwas mehr als einer minute. Aber das wirst du jetzt natürlich nicht glauben - weil du wahrscheinlich auch noch nie mit einer SQLite-DB gearbeitet hast die mehr als 10 MB groß ist...
  • "Ich würde es allerdings nicht darauf ankommen lassen."
    Übrigens geht deine Lösung nicht, wenn du an der eingefügten Stellle nicht mehr vorbei kommst. Das ist ein logisches Problem, keines der Implementierung.

    Mit einem Enumerator mache ich das übrigens über eine Kopie.


    Ich verstehe immer noch nicht, wie du SQLite mit CD vergleichen kannst. Das eine ist eine relationale Datenbank, dass andere ein Modeller für einen Objekt-Graphen. Nebenbei hantiere ich hier mit größeren Datenmengen herum und habe gerade beim Starten keine Probleme mit CoreData erlebt. Das ging immer flott.

    Was man nicht tun darf, ist es, beim Start großartig in den Daten herumzuwühlen. Dann müssen die geladen werden, was natürlich aufs Laufzeitverhalten schlägt. So unterschiedlich dürfte das bei SQLite aber auch nicht sein.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Tom9811
    "Ich würde es allerdings nicht darauf ankommen lassen."
    Übrigens geht deine Lösung nicht, wenn du an der eingefügten Stellle nicht mehr vorbei kommst. Das ist ein logisches Problem, keines der Implementierung.

    Mit einem Enumerator mache ich das übrigens über eine Kopie.


    bitte?

    Original von Tom9811
    Ich verstehe immer noch nicht, wie du SQLite mit CD vergleichen kannst. Das eine ist eine relationale Datenbank, dass andere ein Modeller für einen Objekt-Graphen. Nebenbei hantiere ich hier mit größeren Datenmengen herum und habe gerade beim Starten keine Probleme mit CoreData erlebt. Das ging immer flott.

    Was man nicht tun darf, ist es, beim Start großartig in den Daten herumzuwühlen. Dann müssen die geladen werden, was natürlich aufs Laufzeitverhalten schlägt. So unterschiedlich dürfte das bei SQLite aber auch nicht sein.


    natürlich wühle ich beim starten in den daten rum. Um genau zu sein hole ich sogar 90% der daten in den speicher und erstelle objekte daraus. Ob CoreData das nun aus ner SQLite Datenbank saugt oder aus ner noch viel langsameren XML oder einer binären datei macht es auch nicht schneller ;)
  • bitte?
    Bei einem Enumerator mache ich das über eine Kopie

    Öhm, ich wühle nicht in den Daten herum. Deshalb erzeugt mir Core Data auch keine Objekte im Hauptspeicher. Core Data hält nämlich Buch darüber, was benötigt ist und was nicht.

    Wieso wühlst du denn in den daten herum?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • ich hab schon verstanden dass du ne kopie verwendest aber was bitte soll daran schneller bzw sauberer sein? Beim einfügen machts ja nicht besonders viel sinn weil beim einfügen man eh meist die genaue position kennt und folglich gar keine for oder enumerator braucht. beim entfernen ises aber was anderes.

    Das glaube ich dir gern dass CD VERSUCHT zu wissen was gebruacht wird und was nicht aber ich denke mal als entwickler kann man MANCHMAL selbst besser wissen was gebraucht wird und was nicht. Manchmal macht es eben auch mehr sinn nen startupscreen anzuzeigen und dort was zu laden als 2 sekunden nachdem das programm läuft und was angezeigt werden soll dann die ganzen daten (ja, stell dir vor es sollen die ganzen daten angezeigt werden) geladen werden...
  • Ich verstehe deinen ersten Teil nicht. Es geht doch ums Einfügen bzw. Löschen in einer Schleife!?

    Nein, Core Data ermittelt es ganz einfach: Wenn du auf ein Objekt zugreifst, etwa in einer Relation, dann wird es nicht geladen, sondern ein Proxy erzeugt. Wenn du dann eine Property des Objektes benötigst, dann wird das Objekt geladen. Weniger geht nicht, auch nicht mit dem Wissen des Entwicklers.

    Die ganzen Daten sollen angeziegt werden? Darf ich dich fragen, was deine App macht und welche Daten sie verarbeitet?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Tom9811
    Ich verstehe deinen ersten Teil nicht. Es geht doch ums Einfügen bzw. Löschen in einer Schleife!?

    Ja, es geht ums löschen oder einfügfen. vor allem ums löschen. Erklär mir mal wie du das mit einem enumerator besser machen kannst als mit einer for-schleife?

    Original von Tom9811
    Nein, Core Data ermittelt es ganz einfach: Wenn du auf ein Objekt zugreifst, etwa in einer Relation, dann wird es nicht geladen, sondern ein Proxy erzeugt. Wenn du dann eine Property des Objektes benötigst, dann wird das Objekt geladen. Weniger geht nicht, auch nicht mit dem Wissen des Entwicklers.

    Die ganzen Daten sollen angeziegt werden? Darf ich dich fragen, was deine App macht und welche Daten sie verarbeitet?


    stell dir iTunes vor, nur dass rechts nicht einfach ein tableview ist sondern ein outlineview welches zwischen 5 und 30 columns hat (so viele attribute haben auch jeweils die objekte bzw die SQL-table columns). Jedes der "Lieder in iTunes" ist also nciht nur eine einfahce row sondern hat 0 bis 48 untereinträge welche jeweils in einer anderen SQL-Table liegen ;-). Dann stell dir mal vor du hast 50.000 "lieder" mit jeweils etwa 5 "childs" zu jedem lied. Da kann CoreData lange rumrechen bis es weis was zu laden ist - ich als entwickler weis bereits sofort war alles geladen werden muss ;)
    Die ganze sache kannst du aber nicht nur einfach aus der DB laden wie du gerade die rows brauchst weil du weist nicht welche rows gebraucht werden weil du auch net wiest wie die ganze geschichte sortiert sein soll (bzw die sortierung in der DB stimmt net mit der im programm überein)

    verstanden?
  • Spannend wäre doch -- wenn Ihr hier schon dauern die Performance dauern vergleicht -- wie makeObjectsPerformSelector: dabei abschneidet.

    Nutze ich glaube ich viel zu selten.
    if (!exit(-1)) fprintf(stderr, "exit call failed. Program will continue\n");
  • Ja, könnte auch gut laufen. Aber IMP ist, denke ich, ungeschlagen. Das gilt sowohl für den Enumreator selbst als auch für Methodenaufrufe innerhalb der Enumeration. Dann musst du allerdings peinlich genau aufpassen, dass die Colleciton homogen ist. Sonst gibt IMP natürlich mächtig Ärger.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Was meinst du mit besser? Ich habe doch beschrieben, wie ich es mache.

    In deinem Falle muss Core Data ganz sicher nicht alle Objekte laden. Es lädt diejenigen, die es benötigt. Wenn nur wenige angezeigt werden, werden auch nur diese geladen. Da zu muss Core Data ganz wenig rechnen: 0.

    Was du freilich nicht machen darfst, ist fröhlich durch die Objekte iterieren, also etwa rekursiv alle Unterobjekte abfragen. Dann müssen die natürlich geladen werden. Es ist eben wie bei SQL-Abfragen auch: Man kann die derart verpatzen, dass das DBMS ausflippt. Man kann allerdings es auch mit Überlegung machen. Und so ist es auch bei Core Data. Nur geht die Überlegung ganz gewiss schief, wenn man so tut, als ob Core Data eine Datenbank sei und etwa auf den Gedanken verfällt, die verwiesenen Objekt mit einem Fetch zu holen.

    Du weißt übrigens im Gegensatz dazu nicht ganz sicher, was geladen werden muss. Du weißt nämlich in der Regel nicht, welcher Ausschnitt gerade auf dem Bildschirm ist. Fragst du das jetzt bei deiner Implementierung ab? Kaum! Core Data weiß das indessen, weil in der Relationship Proxys liegen, die erst auf einen Hit nachgeladen werden, also etwa dann, wenn der User eine Zeile tiefer scrollt und nun Attribute abgefragt werden.

    Ich nehme an, du hast dir ebenfalls eine solche auf Proxing beruhende Lazyness von Hand programmiert?

    Verstanden?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Tom9811
    Was meinst du mit besser? Ich habe doch beschrieben, wie ich es mache.

    In deinem Falle muss Core Data ganz sicher nicht alle Objekte laden. Es lädt diejenigen, die es benötigt. Wenn nur wenige angezeigt werden, werden auch nur diese geladen. Da zu muss Core Data ganz wenig rechnen: 0.


    ich habe dich sehr wohl verstanden, zitiere jetzt aber nur das kleine stückchen.
    Jetzt zu einem konkreten problem:
    du hast das outlineview in dem du sagen wir mal die ersten 30 rows siehst. dann muss erstmal rausgefunden werden welche überhaupt die ersten 30 sind weil das ja von der sortierung abhängt. dazu muss man dann schon mal das ganze durchsortieren. irgendwie muss CD das ja auch machen. also müssen dann schon mal die 50.000 einträge gefetcht werden (natürlich nur bestimmte ettribute) und anhand dessen sortiert werden und erst danach die ersten 30 einträge vollständig geladen werden. sehr sinnvoll oder? Wenn dann jetzt jemand hergeht und ein "v" eintippt weil er zu dem ersten eitnrag springen will der mit V beginnt dann musst du die einträge durchiterieren und da werden sie dann geladen. außer du machst es umständlich und schaust welcher buchstabe das im alphabet ist und gehst dann an etwa die stelle in der lsite und suchst dort umher aber das ist ja alles net grad das schönste...
    Vor allem das sortieren wird einiges an zeit brauchen gel. Ich hab mal ein array mit 20.000 einträgen sorteiren lassen und das hat ganz schön lang gedauert. dann hab ich das optimiert (erste 4 zeichen in 2 UInt64 variablen geschoben und dann werden jeweils diese verglichen). Dann läuft das um einiges schneller. das kann CD sicher nicht ;)
    verstehst du was ich meine?