22 berichten aan het bekijken - 1 tot 22 (van in totaal 22)
  • Q:
    Bijdrager
    renssies

    Vreemd UITableView Probleem

    In de 2.0 update van AT hebben mensen vaak last van raar probleem. Wanneer de tabel gereload wordt doormiddel van reloadData. Verdwijnt soms de content en ontstaat er een groot wit vlak, altijd na de 3de rij. Soms wanneer je scrollt verdwijnt het vanzelf maar soms blijft het ook en wordt alles onbruikbaar en wit. Dit is overigens niet alleen in AT maar ook in een andere app waar ik mee bezig ben. Hierbij gebruik ik dezelfde code, vreemd is dat het daar maar op 1 van de 6 UITableViews

    Screenshot 1: http://cl.ly/A8dN
    Screenshot 2: http://cl.ly/A8WU

    Iemand al eerder dit probleem gehad en kan helpen?

    Bijdrager
    mhrenes

    Zonder de code fragmenten te zien kan ik je daar niet verder mee helpen.

    Bijdrager
    renssies

    Ik gok dat het probleem ergens in de cell for row zit. Daarom heb ik die hier gepost

    (sorry was vergeten de code erbij te zetten)

    Code: http://pastebin.com/AkJEJXsQ

    Edit: code even op pastebin gezet, werd niet goed weergegeven in het forum

    Bijdrager
    renssies

    Ik gok dat het probleem ergens in de cell for row zit. Daarom heb ik die hier gepost

    (sorry was vergeten de code erbij te zetten)

    Code: http://pastebin.com/AkJEJXsQ

    Edit: code even op pastebin gezet, werd niet goed weergegeven in het forum

    Bijdrager
    mhrenes

    Het probleem lijkt me niet te zitten in de code die je laat zien. Vermoedelijk zit het dus verder op (waar je de content plaatst) of elders in de code.

    Heb je:
    1. Breakpoint gezet in je cellForRowAtIndexPath:(NSIndexPath *)indexPath method om te zien of die daadwerkelijk aangeroepen wordt als reloadData hebt gebruikt?
    2. Is de bron van je data ondertussen nog wel valide?? Aangezien na een reloadData een aantal cells nog wel data bevatten kan het zijn dat dat hergebruikte cells zijn die gewoon hun oude content laten zien. Check dus in cellForRowAtIndexPath:(NSIndexPath *)indexPath of de je data-bron nog wel klopt/bestaat.
    3. Je roept reloadData wel aan vanaf de main thread?!!!

    Laat wat meer van je code zien…

    Bijdrager
    robinspeijer

    Het lijkt erop dat dit een probleem is met reusing van UITableViewCells.

    Bijdrager
    renssies

    Ik heb naar de eerste 2 punten al gekeken.
    3 was al eerst zo dat hij niet in main thread maar dat heb ik nog voor de release van 2.0 opgelost dus nu gebeurt het wel op de main thread.

    Hier heb ik nog wat meer code waarmee ik de array vul: http://pastebin.com/xnGUiQ2h

    @robin

    Ja daar heb ik op gezocht maar ik zie het probleem niet.

    Bijdrager
    mhrenes

    Ik denk dat ik het probleem al zie.

    Ik vermoed dat downloadTips vanaf een secundaire thread wordt aangeroepen (dus niet de main thread). De reden hiervoor is dat waarschijnlijk:

    <br />
    NSMutableDictionary *storiesData = [renssiesLib jsonFromURLString:url];<br />
     

    uiteindelijk een synchronous request is (oftewel die method returned pas als je alle data ingelezen hebt en blokkeert tot die tijd de user interface indien aangeroepen vanaf de main thread). Als dat zo is dan vermoed ik dat ergens in je code (van renssiesLib) iets verstopt zit zoals bijvoorbeeld

    <br />
    NSData* data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];<br />
     

    Toch roep je een aantal niet thread-safe functies aan vanaf een secundaire thread (setNetworkActivityIndicator, refreshHeaderView en mogelijk ook vanuit dataSourceDidFinishLoadingNewData).

    Wat ik zou doen om te testen of hier het probleem zit:

    Laat downloadTips over de mainThread lopen, als alles nu goed werkt dan moet je je code anders opzetten (meer specifiek: je renssiesLib van een synchrone naar een asynchrone download overzetten).

    Het opzetten van secundaire threads is vaak niet de oplossing voor dit soort problemen maar wel de bron van een heleboel problemen (thread synchronisatie ondermeer en het feit dat UIKit in het algemeen niet threadsafe is). Bedenk ook maar eens wat er gebeurt als je twee keer achter elkaar downloadTips aanroept! Er ontstaat dan een race condition op tipsArray, die kan zomaar halverwege de uitvoering van je code van gedaante veranderen!

    Overigens is er mogelijk ook een memory leak (je alloceert je tipsArray (zo te zien een class variable) maar onduidelijk is wat er met de vorige versie gebeurt).

    Laat even weten wat er gebeurt cq of het klopt, ik ben erg benieuwd.

    Bijdrager
    renssies

    Na dat ik mijn code heb gepost zag ik wat ik fout deed in mijn code en ja inderdaad ik allocate mijn tipsArray in een secundaire thread wat je nooit hoor te doen. Dit heb ik nu opgelost door de dictionary me te sturen naar de main thread en hem daar pas in een array te laden.

    Stom eigenlijk van me want ik weet dat ik dat niet moet doen, ik doe het ook nergens, alleen bij de tips view, niet eens bij een categorie. Maar het is nu opgelost.

    Bijdrager
    bitsflew

    Dit heb ik nu opgelost door de dictionary me te sturen naar de main thread en hem daar pas in een array te laden.

    TIP: Je kunt dergelijke threading problemen heel elegant oplossen door gebruik te maken van blocks (min. iOS 4)

    http://pastebin.com/aMMEsdUH

    BTW Wordt downloadTips maar één keer aangeroepen na het opstarten?

    Bijdrager
    renssies

    Nee meerdere keren, ook bij reloaden en catergorie laden.

    Bijdrager
    bitsflew

    Wat doe je in dat geval met de vorige waarde van tipsArray?

    Bijdrager
    mhrenes

    Ik zou het probleem niet oplossen met threads, pas je code aan en gebruik de asynchrone manier van het ophalen van data met NSURLConnection. Dat is een stuk eleganter en robuuster.
    Je kunt dan ook veel makkelijker omgaan met timeouts of andere errors. Zeker bij een iOS app is de kans vrij aanzienlijk dat je verbinding halverwege faalt of buitengewoon traag is (door 3G/GRPS verbindings problemen).

    Bijdrager
    bitsflew
    mhrenes op 15 september 2011

    Ik zou het probleem niet oplossen met threads, pas je code aan en gebruik de asynchrone manier van het ophalen van data met NSURLConnection. Dat is een stuk eleganter en robuuster.

    Je kunt dan ook veel makkelijker omgaan met timeouts of andere errors. Zeker bij een iOS app is de kans vrij aanzienlijk dat je verbinding halverwege faalt of buitengewoon traag is (door 3G/GRPS verbindings problemen).

    Voor timeouts,errors en robustheid maakt het niets uit of je NSURLConnection in de mainthread of een aparte thread gebruikt.

    Echter de synchrone manier is met GCD en blocks eleganter (en leesbaarder) dan de asynchone methode met delegate callbacks.

    Ook de asynchrone methode wordt met blocks een heel stuk leesbaarder.

    blocks-sample

    Bijdrager
    renssies

    Sorry dat ik even niet gereageerd heb maar ik was met een andere app bezig.

    @bitsflew
    De tipsarray wordt gewoon steeds helemaal leeg gegooid en opnieuw gevuld bij reloaden.

    @mhrenes
    Ik ga eens kijken naar NSURLConnection, ik wou het eerst al te gebruiken maar die delegate callbacks vond ik enorm irritant maar nu bedenk ik me dus dat ik hem natuurlijk ook gewoon synchrone in een andere thread kan laten lopen. Maar we zullen zien of het lukt.

    Het probleem lijkt in ieder geval opgelost, het probleem is niet meer voorgekomen.

    Bijdrager
    bitsflew

    @bitsflew
    De tipsarray wordt gewoon steeds helemaal leeg gegooid en opnieuw gevuld bij reloaden.

    OK

    btw Bij mij ging de alarmbel af na het zien van de volgende code:

    tipsArray = [[NSMutableArray alloc] initWithArray:[storiesData objectForKey:aKey]];

    tipsArray is een instance variable welke overschreven wordt zonder rekening te houden met de vorige waarde, maw een memory leak.

    Maar ik begrijp dat je de oude waarde van tipsArray op een andere plaats released, maw loos alarm.

    Bijdrager
    renssies

    Ja ik release hem inderdaad wel eerst voor ik hem weer vol prop.

    Toch moet ik weer melden dat het probleem niet opgelost is, het probleem is wel wat minder geworden, alleen rij 4 wordt namelijk wit maar zodra je scrollt wordt hij gevuld. Iemand toch nog hoe ik dit kan oplossen?

    Bijdrager
    bitsflew

    Toch moet ik weer melden dat het probleem niet opgelost is,

    Heb je de volgende regels ook naar de mainthread verplaatst?

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

    [refreshHeaderView setCurrentDate];

    BTW Wat gebeurt er in dataSourceDidFinishLoadingNewData?

    Misschien dat daar iets wordt aangeroepen wat niet threadsave is?

    Bijdrager
    mhrenes

    @renssies:
    Heb jij mijn suggestie al geprobeerd (tijdelijk alles over de mainThread laten lopen). Als je probleem dan opgelost is dan weet je dat het door een probleem met threading komt (toch ergens niet threadsave code gebruikt (zoals UIKit))

    @bitsflew:
    Blocks kan natuurlijk maar waarom een probleem wat al keurig opgelost is door Apple (NSURLConnection maakt indien nodig een eigen thread aan voor asynchrone verbindingen) nodeloos ingewikkeld maken? Die callbacks zijn prima te gebruiken en je bent niet verplicht om alle callbacks te implementeren. Dit is overigens ook wat Apple zelf adviseert, zie het hoofdstuk over Concurrency:

    Rely on the system frameworks whenever possible. The best way to achieve concurrency is to take advantage of the built-in concurrency provided by the system frameworks. Many frameworks use threads and other technologies internally to implement concurrent behaviors. When defining your tasks, look to see if an existing framework defines a function or method that does exactly what you want and does so concurrently. Using that API may save you effort and is more likely to give you the maximum concurrency possible.

    Als overigens je bewerking van de data lang duurt dan kun je daar natuurlijk prima blocks voor gebruiken maar dat is een andere discussie.

    Bijdrager
    renssies

    Die suggestie heb ik niet geprobeerd maar ik heb eens gekeken wat er allemaal vanuit een andere thread gedaan wordt. Ik kwam erachter dat het deel wat de pull to refresh naar de goede datum zet en weer laat verdwijnen op de secundaire thread werd aangeroepen. Deze heb ik nu op de main thread gedaan en het probleem lijkt weg te zijn maar ik ga niet te hard roepen, we kijken even met alle testers of het is opgelost.

    Bijdrager
    bitsflew

    @rensis

    Voor de duidelijkheid, asynschoon gebruik van NSURLConnection is niet specifiek bedoeld voor het gebruik in de mainthread!

    Het bedoeld om gebruikt te worden i.c.m. een runloop, dat het werkt in de mainthread komt omdat de mainthread al een runloop heeft.

    Die mainthread aanpak wordt pas echt lastig als je meerdere simultane requests wilt ondersteunen, in iedere delegate callback moet je je ineens gaan afvragen om welke request het gaat om zo de context te achterhalen en voor je het weet zit je zelf een queueing mechanisme te implementeren.

    Een ander probleem is dat je moet uitkijken dat je geen zware dingen gaat doen in die callbacks.

    Daarom gebruik ik NSURLConnection ook asynschroon maar wel in een aparte thread (NSOperation om precies te zijn), op die manier heb ik een queue en elke request heeft z’n eigen context.

    Note. In dit geval ging het om een iOS 3.2 app, maw GCD was helaas geen optie.

    Voor de liefhebbers, de main van NSOperation ziet er als volgt uit:

    http://pastebin.com/rDcSZQAf

    Blocks alleen is geen oplossing voor multitreading, dat is alleen het geval indien het gecombineerd wordt met GCD

    Blocks zijn wel een elegante oplossing voor het implementeren van (delegate) callbacks, je code blijft bij elkaar je hoeft geen context te bewaren voor de callbacks omdat de callback code in dezelfde scope loopt als de API call.

    Het is niet voor niets dat Apple in zowel iOS als OSX block versies heeft van API calls die voorheen gebruik maakten van callbacks!

    Bijdrager
    renssies

    @bitsflew
    Oke zoveel had ik nog niet geleerd :P. Maar ik zal er eens naar gaan kijken.

    Het lijkt erop dat het probleem is verdwenen, alle testers melden geen problemen. Dus bedankt allemaal:)

22 berichten aan het bekijken - 1 tot 22 (van in totaal 22)

Je moet ingelogd zijn om een reactie op dit onderwerp te kunnen geven.