Jump to content

vfbb

Members
  • Content Count

    266
  • Joined

  • Last visited

  • Days Won

    30

Everything posted by vfbb

  1. It is not an issue, it is the cost benefit of higher performance x less memory. But the optimization of creating it with the capacity is valid considering that the performance increases is on average 30%, both for small collections and for large ones. uses System.SysUtils, System.Generics.Collections, System.Diagnostics, FMX.Dialogs; procedure TestWithInitialCapacity; var I, J, LNewId: Integer; LDictionary: TDictionary<Integer, Boolean>; LStopwatch: TStopwatch; begin LStopwatch := TStopwatch.StartNew; for I := 0 to 10000 do begin LDictionary := TDictionary<Integer, Boolean>.Create(200); for J := 0 to 100 do begin repeat LNewId := Random(High(Integer)); until not LDictionary.ContainsKey(LNewId); LDictionary.Add(LNewId, True); end; FreeAndNil(LDictionary); end; showmessage(Format('Test with initial capacity: %g', [LStopwatch.Elapsed.TotalMilliseconds])); end; procedure TestWithoutInitialCapacity; var I, J, LNewId: Integer; LDictionary: TDictionary<Integer, Boolean>; LStopwatch: TStopwatch; begin LStopwatch := TStopwatch.StartNew; for I := 0 to 10000 do begin LDictionary := TDictionary<Integer, Boolean>.Create; for J := 0 to 100 do begin repeat LNewId := Random(High(Integer)); until not LDictionary.ContainsKey(LNewId); LDictionary.Add(LNewId, True); end; FreeAndNil(LDictionary); end; showmessage(Format('Test without initial capacity: %g', [LStopwatch.Elapsed.TotalMilliseconds])); end;
  2. Exactly! The Dictionary is only O(1) in the best case, as there can be many collisions of the key hashes, and this creates a loop that can reach O (n * 3/4) in the worst case, although it is very difficult. But what makes losing more performance are the grows that can occur as teams are added because the implementation maintain the count 50% -75% of the capacity, which I consider high value. But to avoid the many grows, and allow fewer collisions, just create it with the desired capacity. For example, for a collection of 100 items, I simply start with a capacity of 200. LDicionary := TDictionary<TKey, TValue>.Create(200); Remember, when you call Clear from the dictionary, it also removes the capacity, so you better recreate it instead of Clear.
  3. vfbb

    iOS handle incoming url

    @Dave Nottage Hi Dave, I tested and works perfectly!! Thanks you so much, you are the man!! Just to register here the complete solution. (I preferred to patch the delphi source files because I already have others patchs in that files) iOS - Handle incoming url (custom schemes or universal links) In the file iOSapi.Foundation.pas put the code: // Dave Nottage code (https://www.delphiworlds.com/) NSUserActivityPersistentIdentifier = NSString; TNSUserActivityBlockMethod1 = procedure(inputStream: NSInputStream; outputStream: NSOutputStream; error: NSError) of object; TNSUserActivityBlockMethod2 = procedure of object; NSUserActivityClass = interface(NSObjectClass) ['{412EAEBF-5927-4D01-B83F-69D3B5DFE7B5}'] {class} procedure deleteAllSavedUserActivitiesWithCompletionHandler(handler: TNSUserActivityBlockMethod2); cdecl; [MethodName('deleteSavedUserActivitiesWithPersistentIdentifiers:completionHandler:')] {class} procedure deleteSavedUserActivitiesWithPersistentIdentifiers(persistentIdentifiers: NSArray; handler: TNSUserActivityBlockMethod2); cdecl; end; NSUserActivity = interface(NSObject) ['{B8C2F6C9-31FE-4282-B7CA-98C96E163033}'] function activityType: NSString; cdecl; procedure addUserInfoEntriesFromDictionary(otherDictionary: NSDictionary); cdecl; procedure becomeCurrent; cdecl; function delegate: Pointer; cdecl; function expirationDate: NSDate; cdecl; procedure getContinuationStreamsWithCompletionHandler(completionHandler: TNSUserActivityBlockMethod1); cdecl; function initWithActivityType(activityType: NSString): Pointer; cdecl; procedure invalidate; cdecl; function isEligibleForHandoff: Boolean; cdecl; function isEligibleForPrediction: Boolean; cdecl; function isEligibleForPublicIndexing: Boolean; cdecl; function isEligibleForSearch: Boolean; cdecl; function keywords: NSSet; cdecl; function needsSave: Boolean; cdecl; function persistentIdentifier: NSUserActivityPersistentIdentifier; cdecl; function referrerURL: NSURL; cdecl; function requiredUserInfoKeys: NSSet; cdecl; procedure resignCurrent; cdecl; procedure setDelegate(delegate: Pointer); cdecl; procedure setEligibleForHandoff(eligibleForHandoff: Boolean); cdecl; procedure setEligibleForPrediction(eligibleForPrediction: Boolean); cdecl; procedure setEligibleForPublicIndexing(eligibleForPublicIndexing: Boolean); cdecl; procedure setEligibleForSearch(eligibleForSearch: Boolean); cdecl; procedure setExpirationDate(expirationDate: NSDate); cdecl; procedure setKeywords(keywords: NSSet); cdecl; procedure setNeedsSave(needsSave: Boolean); cdecl; procedure setPersistentIdentifier(persistentIdentifier: NSUserActivityPersistentIdentifier); cdecl; procedure setReferrerURL(referrerURL: NSURL); cdecl; procedure setRequiredUserInfoKeys(requiredUserInfoKeys: NSSet); cdecl; procedure setSupportsContinuationStreams(supportsContinuationStreams: Boolean); cdecl; procedure setTargetContentIdentifier(targetContentIdentifier: NSString); cdecl; procedure setTitle(title: NSString); cdecl; procedure setUserInfo(userInfo: NSDictionary); cdecl; procedure setWebpageURL(webpageURL: NSURL); cdecl; function supportsContinuationStreams: Boolean; cdecl; function targetContentIdentifier: NSString; cdecl; function title: NSString; cdecl; function userInfo: NSDictionary; cdecl; function webpageURL: NSURL; cdecl; end; TNSUserActivity = class(TOCGenericImport<NSUserActivityClass, NSUserActivity>) end; ... function NSUserActivityTypeBrowsingWeb: NSString; ... implementation ... function NSUserActivityTypeBrowsingWeb: NSString; begin result := CocoaNSStringConst(FoundationFwk, 'NSUserActivityTypeBrowsingWeb'); end; In the file FMX.Platform.iOS.pas, in the TApplicationDelegate class, in the private section, put the code: class function applicationContinueUserActivityRestorationHandler(self: id; _cmd: SEL; application: PUIApplication; userActivity: Pointer; restorationHandler: Pointer; restorableObjects: Pointer): Boolean; cdecl; static; In the file FMX.Platform.iOS.pas, in the implementation of the method TApplicationDelegate.CreateDelegateMetaClass, before the line "objc_registerClassPair(DelegateClass);", put the code: class_addMethod(DelegateClass, sel_getUid('application:continueUserActivity:restorationHandler:'), @applicationContinueUserActivityRestorationHandler, 'B@:@@@@'); In the file FMX.Platform.iOS.pas, in the TApplicationDelegate implementation, put the code: class function TApplicationDelegate.applicationContinueUserActivityRestorationHandler( self: id; _cmd: SEL; application: PUIApplication; userActivity, restorationHandler, restorableObjects: Pointer): Boolean; var LUserActivity: NSUserActivity; LURLString: string; begin Result := False; if Assigned(userActivity) then begin LUserActivity := TNSUserActivity.Wrap(userActivity); if NSStrToStr(LUserActivity.activityType) = NSStrToStr(NSUserActivityTypeBrowsingWeb) then begin if Assigned(LUserActivity.webpageURL) then LURLString := NSStrToStr(LUserActivity.webpageURL.absoluteString) else LURLString := string.Empty; Result := PlatformCocoaTouch.HandleApplicationEvent(TApplicationEvent.OpenURL, TiOSOpenApplicationContext.Create(string.Empty, LURLString, nil)); end; end; end; Usage uses System.Messaging, FMX.Platform, FMX.Platform.iOS, FMX.Dialogs; constructor TipUrlHandler.Create; begin inherited Create; TMessageManager.DefaultManager.SubscribeToMessage(TApplicationEventMessage, ApplicationEventMessageHandler); end; destructor TipUrlHandler.Destroy; begin TMessageManager.DefaultManager.Unsubscribe(TApplicationEventMessage, ApplicationEventMessageHandler, True); inherited; end; procedure TipUrlHandler.ApplicationEventMessageHandler(const ASender: TObject; const AMessage: TMessage); begin case TApplicationEventData(TApplicationEventMessage(AMessage).Value).Event of TApplicationEvent.OpenUrl: begin Showmessage(TiOSOpenApplicationContext(TApplicationEventData(TApplicationEventMessage(AMessage).Value).Context).URL); end; else end; end;
  4. vfbb

    Memory Management with many objects

    Out of memory I really like to use Grijjy Cloud Logger. You can monitor all objects in your program in real time, and each time you update this list it will indicate the growth (in number of instances and memory) of each class. Best of all, this can be done in both debug mode and release mode. Use this only in the development environment as there will be a small loss of performance. Move to 64 bits I advise you to start writing code with 64-bit support as soon as possible, this is the future, because although microsoft is not forcing this migration yet, several others have already done it, such as Android, iOS and OSX which today only allow 64 bits. It is a trend even from Embarcadero itself since it added support for Linux only 64 bits. But, of course, keep support for 32-bit too, as there may be users with 32-bit windows still and also because the Win32 debugger is better than Win64. 😉
  5. I have a base data class that is used for multithreaded data classess in my project. I made an implementation to reduce thousands of lines of code, which theoretically should work, but even with some successful tests, I am afraid to release a version to the end user without being sure. function TipCustomData.GetSafeValue(var AField: string): string; begin Lock; // Enter critical section try Result := AField; finally Unlock; // Leave critical section end; end; procedure TipCustomData.SetSafeValue(var AField: string; const AValue: string); begin BeginUpdate; // Enter critical section try if AField <> AValue then begin AField := AValue; Change; end; finally EndUpdate; // Leave critical section triggering change event end; end; In the code above, the most intuitive in these cases would be to use the field pointer, but as "var" parameters internally it is already passed as a pointer (regardless of type), so both implementations should work in the same theoretical way (correct me if i'm wrong). Of course, I only access the parameters within the critical section. The implementation of my multithreaded data class, descendant of this, would be for example like this: type TipUser = class(TipCustomData) private FName: string; function GetName: string; procedure SetName(const AValue: string); public property Name: string read GetName write SetName; end; function TipUser.GetName: string; begin Result := GetSafeValue(FName); end; function TipUser.SetName(const AValue: string); begin Result := SetSafeValue(FName, AValue); end; So the question is, is this cleaner implementation that could exist of a multithreaded data class, is it really thread-safe? Sorry for my bad english. 😉
  6. @Hans♫ in the iTunes you can download each user crash report file and view the call stack to identify the problem. You will need to symbolize the crash file using the dSYM file of the version of your app thats crashed. Read about:
  7. Exploring the .crash files generated by the iOS symbolizing it to identify the call stack https://github.com/viniciusfbb/fmx_tutorials/blob/master/delphi_ios_exploring_app_crashes/README.md
  8. vfbb

    This implementation is Thread-safe?

    Yes, the BeginUpdate call Lock internally, and the EndUpdate call Unlock. The same critical section. Just one critical section per object. Yes, my Change event is completely asynchronous, similar to the fmx messaging system (subscribe / unsubscribe), but it uses X threads to send these messages from one thread to another. I don't like the term "big risk" or "low risk" of deadlock. A good design has to be done with the chance of zero deadlock, locking the object just to copy the data to the local var of the thread or else locking only to write local var of the thread in the object (without executing anything inside locking). Example: procedure TipThread.Execute var LName: string; LId: Cardinal; begin // Read copying to local var LUser.Lock; try LName := LUser.Name; LId := LUser.Id; finally LUser.Unlock; end; // Run your code... // Write from local var LUser.BeginUpdate; try LUser.Name := LName; LUser.Id := LId; finally LUser.EndUpdate; end; end; This will never cause a deadlock.
×