Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/260.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Xamarin iOS内存到处泄漏_C#_Ios_Xamarin.ios_Garbage Collection_Xamarin - Fatal编程技术网

C# Xamarin iOS内存到处泄漏

C# Xamarin iOS内存到处泄漏,c#,ios,xamarin.ios,garbage-collection,xamarin,C#,Ios,Xamarin.ios,Garbage Collection,Xamarin,在过去的8个月里,我们一直在使用Xamarin iOS,并开发了一个具有许多屏幕、功能和嵌套控件的非平凡企业应用程序。我们已经按照“推荐”完成了自己的MVVM架构、跨平台BLL和DAL。我们在Android之间共享代码,甚至我们的BLL/DAL也用于我们的web产品 一切都很好,但现在在项目的发布阶段,我们发现基于Xamarin iOS的应用程序中到处都存在无法修复的内存泄漏。我们遵循了所有的“指导原则”来解决这个问题,但现实是C#GC和Obj-C ARC似乎是不兼容的垃圾收集机制,目前它们在m

在过去的8个月里,我们一直在使用Xamarin iOS,并开发了一个具有许多屏幕、功能和嵌套控件的非平凡企业应用程序。我们已经按照“推荐”完成了自己的MVVM架构、跨平台BLL和DAL。我们在Android之间共享代码,甚至我们的BLL/DAL也用于我们的web产品

一切都很好,但现在在项目的发布阶段,我们发现基于Xamarin iOS的应用程序中到处都存在无法修复的内存泄漏。我们遵循了所有的“指导原则”来解决这个问题,但现实是C#GC和Obj-C ARC似乎是不兼容的垃圾收集机制,目前它们在monotouch平台中相互重叠

我们发现的现实情况是,对于任何非平凡的应用程序,本机对象和托管对象之间都会频繁发生硬循环。例如,在使用lambdas或手势识别器的任何地方,这种情况都非常容易发生。再加上MVVM的复杂性,这几乎是一种保证。忽略其中一种情况,将永远无法收集对象的整个图形。这些图形将引诱其他物体进入并像癌症一样生长,最终导致iOS迅速无情地灭绝

Xamarin的回答是毫无兴趣地推迟这一问题,并不切实际地期望“开发人员应该避免这些情况”。仔细考虑这一点,可以发现这是一种承认,在Xamarin中,垃圾收集基本上被破坏了

我现在意识到,在传统的c#NET意义上,Xamarin iOS中并没有真正的“垃圾收集”。您需要采用“垃圾维护”模式,使GC真正移动并完成它的工作,即使这样,它也永远不会是完美的-非确定性的

我的公司投入了大量资金试图阻止我们的应用程序崩溃和/或内存不足。我们基本上必须显式地、递归地处理眼前的每一件该死的事情,并在应用程序中实现垃圾维护模式,以阻止崩溃,并拥有一个我们可以销售的可行产品。我们的客户是支持和宽容的,但我们知道这不能永远持续下去。我们希望Xamarin有一个专门的团队来解决这个问题,并一劳永逸地解决它。不幸的是,看起来不像

问题是,我们的经验是用Xamarin编写的非平凡企业级应用程序的例外还是规则

更新


请参阅Dispeex方法和解决方案的答案。

我已经发布了一个用Xamarin编写的非平凡应用程序。许多其他国家也有

“垃圾收集”不是魔法。如果创建了附着到对象图根的引用,并且从未分离该引用,则不会收集该引用。这不仅适用于Xamarin,也适用于C#on.NET、Java等

button.Click+=(sender,e)=>{…}
是一种反模式,因为您没有对lambda的引用,并且您永远无法从
Click
事件中删除事件处理程序。类似地,在托管对象和非托管对象之间创建引用时,必须小心理解自己在做什么


至于“我们已经完成了我们自己的MVVM arch”,有一些引人注目的MVVM库(、和),它们都非常重视引用/泄漏问题。

我注意到在您的DisposeEx方法中,您在杀死集合的可见单元格之前,先处理了集合视图源和表视图源。我在调试时注意到visible cells属性被设置为空数组,因此,当您开始处理visible cells时,它们不再“存在”,因此它成为一个零元素数组

我注意到的另一件事是,如果不从超级视图中删除参数视图,您将遇到不一致的异常,我特别注意到设置集合视图的布局


除此之外,我还必须在我们这边实现类似的功能。

我使用以下扩展方法来解决这些内存泄漏问题。想想安德的游戏《最后一战》场景,DisposeEx方法就像激光一样,它将所有视图及其连接的对象分离,并以一种不会使应用程序崩溃的方式递归地进行处理

当不再需要UIViewController的主视图控制器时,只需在该视图控制器上调用dispeex()。如果某些嵌套UIView需要处理特殊的内容,或者您不希望处理它,请实现isSpecialDisposable.SpecialDispose,它被调用来代替IDisposable.dispose

注意:假设应用程序中没有共享UIImage实例。如果是,请修改DisposeEx以智能地进行处置

    public static void DisposeEx(this UIView view) {
        const bool enableLogging = false;
        try {
            if (view.IsDisposedOrNull())
                return;

            var viewDescription = string.Empty;

            if (enableLogging) {
                viewDescription = view.Description;
                SystemLog.Debug("Destroying " + viewDescription);
            }

            var disposeView = true;
            var disconnectFromSuperView = true;
            var disposeSubviews = true;
            var removeGestureRecognizers = false; // WARNING: enable at your own risk, may causes crashes
            var removeConstraints = true;
            var removeLayerAnimations = true;
            var associatedViewsToDispose = new List<UIView>();
            var otherDisposables = new List<IDisposable>();

            if (view is UIActivityIndicatorView) {
                var aiv = (UIActivityIndicatorView)view;
                if (aiv.IsAnimating) {
                    aiv.StopAnimating();
                }
            } else if (view is UITableView) {
                var tableView = (UITableView)view;

                if (tableView.DataSource != null) {
                    otherDisposables.Add(tableView.DataSource);
                }
                if (tableView.BackgroundView != null) {
                    associatedViewsToDispose.Add(tableView.BackgroundView);
                }

                tableView.Source = null;
                tableView.Delegate = null;
                tableView.DataSource = null;
                tableView.WeakDelegate = null;
                tableView.WeakDataSource = null;
                associatedViewsToDispose.AddRange(tableView.VisibleCells ?? new UITableViewCell[0]);
            } else if (view is UITableViewCell) {
                var tableViewCell = (UITableViewCell)view;
                disposeView = false;
                disconnectFromSuperView = false;
                if (tableViewCell.ImageView != null) {
                    associatedViewsToDispose.Add(tableViewCell.ImageView);
                }
            } else if (view is UICollectionView) {
                var collectionView = (UICollectionView)view;
                disposeView = false; 
                if (collectionView.DataSource != null) {
                    otherDisposables.Add(collectionView.DataSource);
                }
                if (!collectionView.BackgroundView.IsDisposedOrNull()) {
                    associatedViewsToDispose.Add(collectionView.BackgroundView);
                }
                //associatedViewsToDispose.AddRange(collectionView.VisibleCells ?? new UICollectionViewCell[0]);
                collectionView.Source = null;
                collectionView.Delegate = null;
                collectionView.DataSource = null;
                collectionView.WeakDelegate = null;
                collectionView.WeakDataSource = null;
            } else if (view is UICollectionViewCell) {
                var collectionViewCell = (UICollectionViewCell)view;
                disposeView = false;
                disconnectFromSuperView = false;
                if (collectionViewCell.BackgroundView != null) {
                    associatedViewsToDispose.Add(collectionViewCell.BackgroundView);
                }
            } else if (view is UIWebView) {
                var webView = (UIWebView)view;
                if (webView.IsLoading)
                    webView.StopLoading();
                webView.LoadHtmlString(string.Empty, null); // clear display
                webView.Delegate = null;
                webView.WeakDelegate = null;
            } else if (view is UIImageView) {
                var imageView = (UIImageView)view;
                if (imageView.Image != null) {
                    otherDisposables.Add(imageView.Image);
                    imageView.Image = null;
                }
            } else if (view is UIScrollView) {
                var scrollView = (UIScrollView)view;
                // Comment out extension method
                //scrollView.UnsetZoomableContentView();
            }

            var gestures = view.GestureRecognizers;
            if (removeGestureRecognizers && gestures != null) {
                foreach(var gr in gestures) {
                    view.RemoveGestureRecognizer(gr);
                    gr.Dispose();
                }
            }

            if (removeLayerAnimations && view.Layer != null) {
                view.Layer.RemoveAllAnimations();
            }

            if (disconnectFromSuperView && view.Superview != null) {
                view.RemoveFromSuperview();
            }

            var constraints = view.Constraints;
            if (constraints != null && constraints.Any() && constraints.All(c => c.Handle != IntPtr.Zero)) {
                view.RemoveConstraints(constraints);
                foreach(var constraint in constraints) {
                    constraint.Dispose();
                }
            }

            foreach(var otherDisposable in otherDisposables) {
                otherDisposable.Dispose();
            }

            foreach(var otherView in associatedViewsToDispose) {
                otherView.DisposeEx();
            }

            var subViews = view.Subviews;
            if (disposeSubviews && subViews != null) {
                subViews.ForEach(DisposeEx);
            }                   

            if (view is ISpecialDisposable) {
                ((ISpecialDisposable)view).SpecialDispose();
            } else if (disposeView) {
                if (view.Handle != IntPtr.Zero)
                    view.Dispose();
            }

            if (enableLogging) {
                SystemLog.Debug("Destroyed {0}", viewDescription);
            }

        } catch (Exception error) {
            SystemLog.Exception(error);
        }
    }

    public static void RemoveAndDisposeChildSubViews(this UIView view) {
        if (view == null)
            return;
        if (view.Handle == IntPtr.Zero)
            return;
        if (view.Subviews == null)
            return;
        view.Subviews.ForEach(RemoveFromSuperviewAndDispose);
    }

    public static void RemoveFromSuperviewAndDispose(this UIView view) {
        view.RemoveFromSuperview();
        view.DisposeEx();
    }

    public static bool IsDisposedOrNull(this UIView view) {
        if (view == null)
            return true;

        if (view.Handle == IntPtr.Zero)
            return true;;

        return false;
    }

    public interface ISpecialDisposable {
        void SpecialDispose();
    }
publicstaticvoiddisposeex(此UIView){
const bool enableLogging=false;
试一试{
if(view.IsDisposedOrNull())
返回;
var viewsdescription=string.Empty;
如果(启用日志记录){
viewsdescription=视图.描述;
调试(“销毁”+视图描述);
}
var disposeView=真;
var disconnectFromSuperView=true;
var disposeSubviews=true;
var removeGestureRecognizers=false;//警告:启用此项可能会导致崩溃,风险自负
var removeConstraints=true;
var removeLayerAnimations=真;
var associatedViewsToDispose=新列表();
var otherDisposables=新列表();
如果(视图为UIActivityIndicatorView){
var aiv=(UIActivityIndicatorView)视图;
如果(aiv.IsAnimating){
aiv.停止设置动画();
}
}else if(视图为UITableView){
var tableView=(UITableView)视图;
if(tableView.DataSource!=null){
添加(tableView.DataSource);
}
if(tableView.BackgroundView!=null){
关联vi
public class Test3Controller : UITableViewController
{
    public Test3Controller () : base (UITableViewStyle.Grouped)
    {
    }
}
var controller = new Test3Controller ();

controller.Dispose ();

controller = null;

GC.Collect (GC.MaxGeneration, GCCollectionMode.Forced);
public class MyViewController : UIViewController
{
    public string Id { get; set; }
}
public class MyViewController : UIViewController
{
    public override void ViewDidAppear(bool animated)
    {
        base.ViewDidAppear (animated);
        MyButton.TouchUpInside =+ DoSomething;
    }

    void DoSomething (object sender, EventArgs e) { ... }
}
public override void ViewDidDisappear(bool animated)
{
    ProcessButton.TouchUpInside -= DoSomething;
    base.ViewDidDisappear (animated);
}
public partial class MyViewController : UIViewController
{
    #if DEBUG
    static int _counter;
    #endif

    protected MyViewController  (IntPtr handle) : base (handle)
    {
        #if DEBUG
        Interlocked.Increment (ref _counter);
        Debug.WriteLine ("MyViewController Instances {0}.", _counter);
        #endif
     }

    #if DEBUG
    ~MyViewController()
    {
        Debug.WriteLine ("ViewController deleted, {0} instances left.", 
                         Interlocked.Decrement(ref _counter));
    }
    #endif
}