Closed nverinaud closed 10 years ago
Hm, I'll look into this. You might have to use Link SDK Only with RxUI, as it will always use a lot of reflection and other things a linker won't pick up
Okay, Thanks !
If I can help spot reflection issues, tell me just how to spot them and I'll do my best :)
I probably need to talk to the Mono guys, it'd be nice if libraries could embed hint information (maybe we just need [Preserve]
tags? Are they respected in libraries?)
Should try it, it is suggested by the stacktrace... :')
Even if I link SDK assemblies only it crashes.
I just spot this one : System.MissingMethodException: Default constructor not found for type System.ComponentModel.ReferenceConverter
.
Are you using Alpha build of Xamarin? If not you probably need to rebuild RxUI
No and not working after rebuilt.
It is a huge issue. I have to drop this library for now because having a 40Mb app instead of 8Mb is not possible. :(
@nverinaud Alright, I was able to repro your bug, try dropping this into your AppDelegate's FinishedLaunching:
// NB: GrossHackAlertTiem™:
//
// Monotouch appears to not load assemblies when you request them
// via Type.GetType, unlike every other platform (even
// Xamarin.Android). So, we've got to manually do what RxUI and
// Akavache would normally do for us
var r = new ModernDependencyResolver();
(new ReactiveUI.Registrations()).Register((f,t) => r.Register(f, t));
(new ReactiveUI.Cocoa.Registrations()).Register((f,t) => r.Register(f, t));
(new ReactiveUI.Mobile.Registrations()).Register((f,t) => r.Register(f, t));
(new Akavache.Registrations()).Register(r.Register);
(new Akavache.Mobile.Registrations()).Register(r.Register);
(new Akavache.Sqlite3.Registrations()).Register(r.Register);
RxApp.DependencyResolver = r;
I'm not sure how the app size relates to the code that Paul posted. Is that another issue?
When you are comparing sizes, are you comparing a native objective-c app versus a Mono-based app?
I'm thinking about porting a currently native app on Android and iOS to ReactiveUI and Xamarin, but size is a bit of a concern to me as well.
On Thu, Dec 5, 2013 at 8:13 AM, Paul Betts notifications@github.com wrote:
@nverinaud https://github.com/nverinaud Alright, I was able to repro your bug, try dropping this into your AppDelegate's FinishedLaunching:
// NB: GrossHackAlertTiem™://// Monotouch appears to not load assemblies when you request them // via Type.GetType, unlike every other platform (even // Xamarin.Android). So, we've got to manually do what RxUI and // Akavache would normally do for usvar r = new ModernDependencyResolver();(new ReactiveUI.Registrations()).Register((f,t) => r.Register(f, t));(new ReactiveUI.Cocoa.Registrations()).Register((f,t) => r.Register(f, t));(new ReactiveUI.Mobile.Registrations()).Register((f,t) => r.Register(f, t));(new Akavache.Registrations()).Register(r.Register);(new Akavache.Mobile.Registrations()).Register(r.Register);(new Akavache.Sqlite3.Registrations()).Register(r.Register);RxApp.DependencyResolver = r;
— Reply to this email directly or view it on GitHubhttps://github.com/reactiveui/ReactiveUI/issues/395#issuecomment-29896193 .
Mono adds approx 2-3Mb to apps with the linker enabled. If you disable the linker (to prevent crash with ReactiveUI) the app weights approx 30Mb more >.<
@paulcbetts Just for testing I tried your starter-mobile repo (https://github.com/paulcbetts/starter-mobile). I removed Akavache.SQlite3 because it does not build and I use the linker in 'Release' mode. I can provide a sample repo preconfigured if you want.
Here is the test project : https://github.com/nverinaud/reactiveui-ios-linker-crash
Thanks for the clarification. :-)
On Thu, Dec 5, 2013 at 8:52 AM, Nicolas VERINAUD notifications@github.comwrote:
Here is the test project : https://github.com/nverinaud/reactiveui-ios-linker-crash
— Reply to this email directly or view it on GitHubhttps://github.com/reactiveui/ReactiveUI/issues/395#issuecomment-29898685 .
Works for me once I fix the stripped setter by adding
TheGuid.Text = "";
ReactiveUI is always going to have a few issues with the linker set to "All Assemblies" because it uses reflection to set properties. They're usually not hard to get around though
Okay for this project it was easy. I don't link against "All Assemblies", only against "SDK Assemblies only".
I do not agree though that it is not "hard" to get around. This one for example :disappointed: :
System.ArgumentException: Set Method not found for 'Placeholder'
at System.Reflection.MonoProperty.SetValue (System.Object obj, System.Object value, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] index, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
at System.Reflection.PropertyInfo.SetValue (System.Object obj, System.Object value, System.Object[] index) [0x00000] in <filename unknown>:0
at ReactiveUI.Reflection+<propWriterCache>c__AnonStorey41.<>m__B9 (System.Object y, System.Object v) [0x00000] in <filename unknown>:0
at ReactiveUI.PropertyBinderImplementation+<bindToDirect>c__AnonStorey55`2[MonoTouch.UIKit.UISearchBar,System.String].<>m__103 (System.String x) [0x00000] in <filename unknown>:0
at System.Reactive.AnonymousSafeObserver`1[System.String].OnNext (System.String value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.SelectMany`2+_+ι[System.String,System.String].OnNext (System.String value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Return`1+_[System.String].Invoke () [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.Scheduler.Invoke (IScheduler scheduler, System.Action action) [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.ImmediateScheduler.Schedule[Action] (System.Action state, System.Func`3 action) [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.Scheduler.Schedule (IScheduler scheduler, System.Action action) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Return`1+_[System.String].Run () [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Return`1[System.String].Run (IObserver`1 observer, IDisposable cancel, System.Action`1 setSink) [0x00000] in <filename unknown>:0
at System.Reactive.Producer`1[System.String].SubscribeRaw (IObserver`1 observer, Boolean enableSafeguard) [0x00000] in <filename unknown>:0
at System.ObservableExtensions.SubscribeSafe[String] (IObservable`1 source, IObserver`1 observer) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.SelectMany`2+_[System.String,System.String].SubscribeInner (IObservable`1 inner) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.SelectMany`2+_[System.String,System.String].OnNext (System.String value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Select`2+_[ReactiveUI.IObservedChange`2[CoEPOC.Core.SearchViewModel,System.String],System.String].OnNext (IObservedChange`2 value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.DistinctUntilChanged`2+_[ReactiveUI.IObservedChange`2[CoEPOC.Core.SearchViewModel,System.String],System.String].OnNext (IObservedChange`2 value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Select`2+_[ReactiveUI.IObservedChange`2[System.Object,System.Object],ReactiveUI.IObservedChange`2[CoEPOC.Core.SearchViewModel,System.String]].OnNext (IObservedChange`2 value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Where`1+_[ReactiveUI.IObservedChange`2[System.Object,System.Object]].OnNext (IObservedChange`2 value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Switch`1+_+ι[ReactiveUI.IObservedChange`2[System.Object,System.Object]].OnNext (IObservedChange`2 value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.Concat`1+_[ReactiveUI.IObservedChange`2[System.Object,System.Object]].OnNext (IObservedChange`2 value) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.ToObservable`1+_[ReactiveUI.IObservedChange`2[System.Object,System.Object]].LoopRec (System.Reactive.Linq.Observαble.State state, System.Action`1 recurse) [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.Scheduler+<InvokeRec1>c__AnonStoreyA`1[System.Reactive.Linq.Observαble.ToObservable`1+_+State[ReactiveUI.IObservedChange`2[System.Object,System.Object]]].<>m__C (System.Reactive.Linq.Observαble.State state1) [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.Scheduler.InvokeRec1[State] (IScheduler scheduler, Pair`2 pair) [0x00000] in <filename unknown>:0
at (wrapper delegate-invoke) System.Func`3<System.Reactive.Concurrency.IScheduler, System.Reactive.Concurrency.Scheduler/Pair`2<System.Reactive.Linq.Observαble.ToObservable`1/_/State<ReactiveUI.IObservedChange`2<object, object>>, System.Action`2<System.Reactive.Linq.Observαble.ToObservable`1/_/State<ReactiveUI.IObservedChange`2<object, object>>, System.Action`1<System.Reactive.Linq.Observαble.ToObservable`1/_/State<ReactiveUI.IObservedChange`2<object, object>>>>>, System.IDisposable>:invoke_TResult__this___T1_T2 (System.Reactive.Concurrency.IScheduler,System.Reactive.Concurrency.Scheduler/Pair`2<System.Reactive.Linq.Observαble.ToObservable`1/_/State<ReactiveUI.IObservedChange`2<object, object>>, System.Action`2<System.Reactive.Linq.Observαble.ToObservable`1/_/State<ReactiveUI.IObservedChange`2<object, object>>, System.Action`1<System.Reactive.Linq.Observαble.ToObservable`1/_/State<ReactiveUI.IObservedChange`2<object, object>>>>>)
at System.Reactive.Concurrency.ImmediateScheduler.Schedule[Pair`2] (Pair`2 state, System.Func`3 action) [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.Scheduler.Schedule[State] (IScheduler scheduler, System.Reactive.Linq.Observαble.State state, System.Action`2 action) [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.ToObservable`1+_[ReactiveUI.IObservedChange`2[System.Object,System.Object]].Run () [0x00000] in <filename unknown>:0
at System.Reactive.Linq.Observαble.ToObservable`1[ReactiveUI.IObservedChange`2[System.Object,System.Object]].Run (IObserver`1 observer, IDisposable cancel, System.Action`1 setSink) [0x00000] in <filename unknown>:0
at System.Reactive.Producer`1[ReactiveUI.IObservedChange`2[System.Object,System.Object]].SubscribeRaw (IObserver`1 observer, Boolean enableSafeguard) [0x00000] in <filename unknown>:0
at System.ObservableExtensions.SubscribeSafe[IObservedChange`2] (IObservable`1 source, IObserver`1 observer) [0x00000] in <filename unknown>:0
at System.Reactive.TailRecursiveSink`1[ReactiveUI.IObservedChange`2[System.Object,System.Object]].MoveNext () [0x00000] in <filename unknown>:0
at System.Reactive.Concurrency.AsyncLock.Wait (System.Action action) [0x00000] in <filename unknown>:0
Thanks for your help.
It's unfortunate that reflection is not well handled by the linker :worried:
Hum… It's actually not that hard, you're right. Needs a bit of documentation and explanations (maybe in a "warning" section somewhere for Xamarin.iOS).
@nverinaud Yeah, you don't even need to set a live object, just create a dummy method that sets a parameter and never call that method
@nverinaud @paulcbetts I exactly run into "System.ArgumentException: Set Method not found for 'Placeholder'" and wondered why it worked on the iOS Sim, but not on the device (throwing that exception). Can you give a concrete example on how to "...just create a dummy method that sets a parameter ..." ? I'm a bit lost here... (And of course would like to use linking :-)
Just create a class with a method and do not use it. Here is an example of what I do.
private static class LinkerFixer
{
private static void KeepTheseStuff()
{
UITextField textField = null;
textField.Placeholder = "";
}
}
This prevents the linker from removing the Placholder
setter from UITextField
.
To identify which methods may be removed simply look where in your code you use BindTo
.
@nverinaud Not much voodo indeed - perfect! Thx ;-)
Using Xamarin.iOS.
When building in
Release | Ad Hoc | App Store
for the device the Linker comes in and seems to remove necessary methods inRxApp
. Setting "Don't link" option when building resolve the issue but it would be great if the Linker could do his job correctly. A hint is to mark "hard-to-discover" methods using the[Preserve]
attribute.Unfortunately I can't easily figure out which methods are "hard-to-discover".
Here is a stack trace when building starter-mobile in
Release
for the device :